-
Notifications
You must be signed in to change notification settings - Fork 91
feat: Adding private wallet functionality with the integration tests #1482
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
Merged
MantisClone
merged 49 commits into
RequestNetwork:master
from
nkoreli:hinkal-private-wallet-v2
Dec 21, 2024
Merged
Changes from all commits
Commits
Show all changes
49 commits
Select commit
Hold shift + click to select a range
e94fb04
Adding Private Wallet Functionality with Working Example
nkoreli 3bf83a5
Sorting Hinkal Dependencies
nkoreli 25e893a
refactor: address initial comments
giorgi-kiknavelidze a795657
refactor: address initial comments
giorgi-kiknavelidze d162cad
refactor: address secondary comments
giorgi-kiknavelidze 8f26021
updated-hinkal-to-0.1.2
nkoreli bdc8415
Merge branch 'master' into hinkal-private-wallet-v2
MantisClone ceb3aa6
adding-integration-tests
nkoreli 07121f2
enriching-tests-in-pr
nkoreli 0949cfd
lint-error
nkoreli 950411c
Merge branch 'master' into hinkal-private-wallet-v2
nkoreli c4fa70f
yarn.lock-update-2
nkoreli 8e51873
skipping-tests-to-accelerate
nkoreli b58ece8
refactor-hinkal-tests-and-change-naming
nkoreli ec9bcb6
Merge branch 'master' into hinkal-private-wallet-v2
nkoreli ef769f0
adding-right-approval-amount-to-erc20privateproxy
nkoreli 3cd3f7d
format file with prettier
giorgi-kiknavelidze 4f2ea88
adding-fee-in-test-case
nkoreli ba65885
Merge branch 'RequestNetwork:master' into hinkal-private-wallet-v2
nkoreli aabc050
update-hinkal-to-latest
nkoreli c8bffb3
Merge branch 'master' into pr/nkoreli/1482
MantisClone 40ed485
refactor: reduce jest timeout to 30 seconds for hinkal tests
MantisClone 6b2bd36
Remove waitLittle when transfering funds to public addresses
MantisClone 72ddf53
fix: add space between tests
MantisClone a8e1f33
refactor: improve error message
MantisClone ab2aad7
Revert "refactor: reduce jest timeout to 30 seconds for hinkal tests"
MantisClone a9bedda
fix: Increase "no output" timeout to 30 mins from 10 mins
MantisClone 8a0a684
Update comment to show test needs both ETH and USDC
MantisClone edde416
Separate hinkal tests from main test suite. Run them monthly.
MantisClone 5fee704
[REVERT ME] run hinkal tests on every commit
MantisClone 4dbab72
Define hinkal payee and payer as environment variables
MantisClone 04bd424
Increase CircleCI no_output_timeout to 30m for test-monthly
MantisClone eb3c48f
[REVERT ME] Set CircleCI no_output_timeout=30m for integration tests
MantisClone 048e5d5
Revert "Remove waitLittle when transfering funds to public addresses"
MantisClone 06e3257
Add Hinkal env vars to test-monthly job
MantisClone c5a1434
fix: build error due to type conflict (env var can be undefined)
MantisClone 84bbaba
Add quotes around CircleCI Environment Variable
MantisClone 4ddad72
replace mnemonic with priv key. CircleCI chokes on env vars with spaces
MantisClone 26b4ae1
fix: config.yml to pass new PRIVATE_KEY env var
MantisClone 1cf6f4a
Debug invalid hexlify error when creating wallet from private key
MantisClone 8aa248a
Revert "Debug invalid hexlify error when creating wallet from private…
MantisClone f2a9a6d
fix: Using CircleCI env vars requires brackets like ${VAR}
MantisClone 5a7ab2b
fix: wrap env var usage in "" quotes
MantisClone e9ed39f
try removing environment from config.yml entirely
MantisClone aef7cb0
refactor: eliminate ADDRESS env vars
MantisClone 27e04e0
fix: addToHinkalStore before calling getRecipientInfo
MantisClone b31986d
Revert "[REVERT ME] Set CircleCI no_output_timeout=30m for integratio…
MantisClone 67f472f
Revert "[REVERT ME] run hinkal tests on every commit"
MantisClone bf6e617
Increase monthly resource class to large
MantisClone File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
215 changes: 215 additions & 0 deletions
215
packages/payment-processor/src/payment/erc-20-private-payment-hinkal.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,215 @@ | ||
| import { Contract, ContractTransaction, Signer, BigNumberish, providers, BigNumber } from 'ethers'; | ||
| import { erc20FeeProxyArtifact } from '@requestnetwork/smart-contracts'; | ||
| import { | ||
| ERC20FeeProxy__factory, | ||
| ERC20Proxy__factory, | ||
| ERC20__factory, | ||
| } from '@requestnetwork/smart-contracts/types'; | ||
| import { ClientTypes, ExtensionTypes } from '@requestnetwork/types'; | ||
| import { Erc20PaymentNetwork, getPaymentNetworkExtension } from '@requestnetwork/payment-detection'; | ||
| import { EvmChains } from '@requestnetwork/currency'; | ||
| import { | ||
| getAmountToPay, | ||
| getProvider, | ||
| getRequestPaymentValues, | ||
| getSigner, | ||
| validateRequest, | ||
| validateErc20FeeProxyRequest, | ||
| getProxyAddress, | ||
| } from './utils'; | ||
| import { IPreparedPrivateTransaction } from './prepared-transaction'; | ||
|
|
||
| import { emporiumOp, IHinkal, RelayerTransaction } from '@hinkal/common'; | ||
| import { prepareEthersHinkal } from '@hinkal/common/providers/prepareEthersHinkal'; | ||
|
|
||
| /** | ||
| * This is a globally accessible state variable exported for use in other parts of the application or tests. | ||
| */ | ||
| export const hinkalStore: Record<string, IHinkal> = {}; | ||
|
|
||
| /** | ||
| * Adds an IHinkal instance to the Hinkal store for a given signer. | ||
| * | ||
| * This function checks if an IHinkal instance already exists for the provided signer’s address in the `hinkalStore`. | ||
| * If it does not exist, it initializes the instance using `prepareEthersHinkal` and stores it. The existing or newly | ||
| * created instance is then returned. | ||
| * | ||
| * @param signer - The signer for which the IHinkal instance should be added or retrieved. | ||
| */ | ||
| export async function addToHinkalStore(signer: Signer): Promise<IHinkal> { | ||
| const address = await signer.getAddress(); | ||
| if (!hinkalStore[address]) { | ||
| hinkalStore[address] = await prepareEthersHinkal(signer); | ||
| } | ||
| return hinkalStore[address]; | ||
| } | ||
|
|
||
| /** | ||
| * Sends ERC20 tokens into a Hinkal shielded address. | ||
| * | ||
| * @param signerOrProvider the Web3 provider, or signer. | ||
| * @param tokenAddress - The address of the ERC20 token being sent. | ||
| * @param amount - The amount of tokens to send. | ||
| * @param recipientInfo - (Optional) The shielded address of the recipient. If provided, the tokens will be deposited into the recipient's shielded balance. If not provided, the deposit will increase the sender's shielded balance. | ||
| * | ||
| * @returns A promise that resolves to a `ContractTransaction`. | ||
| */ | ||
| export async function sendToHinkalShieldedAddressFromPublic( | ||
| signerOrProvider: providers.Provider | Signer = getProvider(), | ||
| tokenAddress: string, | ||
| amount: BigNumberish, | ||
| recipientInfo?: string, | ||
| ): Promise<ContractTransaction> { | ||
| const signer = getSigner(signerOrProvider); | ||
| const hinkalObject = await addToHinkalStore(signer); | ||
|
|
||
| const amountToPay = BigNumber.from(amount).toBigInt(); | ||
| if (recipientInfo) { | ||
| return hinkalObject.depositForOther([tokenAddress], [amountToPay], recipientInfo); | ||
| } else { | ||
| return hinkalObject.deposit([tokenAddress], [amountToPay]); | ||
| } | ||
| } | ||
nkoreli marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * Processes a transaction to pay privately a request through the ERC20 fee proxy contract. | ||
| * @param request request to pay. | ||
| * @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum. | ||
| * @param amount optionally, the amount to pay. Defaults to remaining amount of the request. | ||
| */ | ||
| export async function payErc20ProxyRequestFromHinkalShieldedAddress( | ||
| request: ClientTypes.IRequestData, | ||
| signerOrProvider: providers.Provider | Signer = getProvider(), | ||
| amount?: BigNumberish, | ||
| ): Promise<RelayerTransaction> { | ||
| const signer = getSigner(signerOrProvider); | ||
| const hinkalObject = await addToHinkalStore(signer); | ||
|
|
||
| const { amountToPay, tokenAddress, ops } = prepareErc20ProxyPaymentFromHinkalShieldedAddress( | ||
| request, | ||
| amount, | ||
| ); | ||
|
|
||
| return hinkalObject.actionPrivateWallet( | ||
| [tokenAddress], | ||
| [-amountToPay], | ||
| [false], | ||
| ops, | ||
| ) as Promise<RelayerTransaction>; | ||
| } | ||
MantisClone marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * Processes a transaction to pay privately a request through the ERC20 fee proxy. | ||
| * @param request request to pay. | ||
| * @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum. | ||
| * @param amount optionally, the amount to pay. Defaults to remaining amount of the request. | ||
| * @param feeAmount optionally, the fee amount to pay. Defaults to the fee amount of the request. | ||
| */ | ||
| export async function payErc20FeeProxyRequestFromHinkalShieldedAddress( | ||
| request: ClientTypes.IRequestData, | ||
| signerOrProvider: providers.Provider | Signer = getProvider(), | ||
| amount?: BigNumberish, | ||
| feeAmount?: BigNumberish, | ||
| ): Promise<RelayerTransaction> { | ||
| const signer = getSigner(signerOrProvider); | ||
| const hinkalObject = await addToHinkalStore(signer); | ||
|
|
||
| const { amountToPay, tokenAddress, ops } = prepareErc20FeeProxyPaymentFromHinkalShieldedAddress( | ||
| request, | ||
| amount, | ||
| feeAmount, | ||
| ); | ||
|
|
||
| return hinkalObject.actionPrivateWallet( | ||
| [tokenAddress], | ||
| [-amountToPay], | ||
| [false], | ||
| ops, | ||
| ) as Promise<RelayerTransaction>; | ||
| } | ||
|
|
||
| /** | ||
| * Prepare the transaction to privately pay a request through the ERC20 proxy contract, can be used with a Multisig contract. | ||
| * @param request request to pay | ||
| * @param amount optionally, the amount to pay. Defaults to remaining amount of the request. | ||
| */ | ||
| export function prepareErc20ProxyPaymentFromHinkalShieldedAddress( | ||
| request: ClientTypes.IRequestData, | ||
| amount?: BigNumberish, | ||
| ): IPreparedPrivateTransaction { | ||
| validateRequest(request, ExtensionTypes.PAYMENT_NETWORK_ID.ERC20_PROXY_CONTRACT); | ||
|
|
||
| const { value: tokenAddress } = request.currencyInfo; | ||
| const proxyAddress = getProxyAddress( | ||
| request, | ||
| Erc20PaymentNetwork.ERC20ProxyPaymentDetector.getDeploymentInformation, | ||
| ); | ||
|
|
||
| const tokenContract = new Contract(tokenAddress, ERC20__factory.createInterface()); | ||
| const proxyContract = new Contract(proxyAddress, ERC20Proxy__factory.createInterface()); | ||
|
|
||
| const { paymentReference, paymentAddress } = getRequestPaymentValues(request); | ||
| const amountToPay = getAmountToPay(request, amount); | ||
|
|
||
| const ops = [ | ||
| emporiumOp(tokenContract, 'approve', [proxyContract.address, amountToPay]), | ||
| emporiumOp(proxyContract, 'transferFromWithReference', [ | ||
| tokenAddress, | ||
| paymentAddress, | ||
| amountToPay, | ||
| `0x${paymentReference}`, | ||
| ]), | ||
| ]; | ||
|
|
||
| return { | ||
| amountToPay: amountToPay.toBigInt(), | ||
| tokenAddress, | ||
| ops, | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Prepare the transaction to privately pay a request through the ERC20 fee proxy contract, can be used with a Multisig contract. | ||
| * @param request request to pay | ||
| * @param amount optionally, the amount to pay. Defaults to remaining amount of the request. | ||
| * @param feeAmountOverride optionally, the fee amount to pay. Defaults to the fee amount of the request. | ||
| */ | ||
| export function prepareErc20FeeProxyPaymentFromHinkalShieldedAddress( | ||
| request: ClientTypes.IRequestData, | ||
| amount?: BigNumberish, | ||
| feeAmountOverride?: BigNumberish, | ||
| ): IPreparedPrivateTransaction { | ||
| validateErc20FeeProxyRequest(request, amount, feeAmountOverride); | ||
|
|
||
| const { value: tokenAddress, network } = request.currencyInfo; | ||
| EvmChains.assertChainSupported(network!); | ||
| const pn = getPaymentNetworkExtension(request); | ||
| const proxyAddress = erc20FeeProxyArtifact.getAddress(network, pn?.version); | ||
|
|
||
| const tokenContract = new Contract(tokenAddress, ERC20__factory.createInterface()); | ||
| const proxyContract = new Contract(proxyAddress, ERC20FeeProxy__factory.createInterface()); | ||
|
|
||
| const { paymentReference, paymentAddress, feeAddress, feeAmount } = | ||
| getRequestPaymentValues(request); | ||
| const amountToPay = getAmountToPay(request, amount); | ||
| const feeToPay = String(feeAmountOverride || feeAmount || 0); | ||
| const totalAmount = amountToPay.add(BigNumber.from(feeToPay)); | ||
|
|
||
| const ops = [ | ||
| emporiumOp(tokenContract, 'approve', [proxyContract.address, totalAmount]), | ||
| emporiumOp(proxyContract, 'transferFromWithReferenceAndFee', [ | ||
| tokenAddress, | ||
| paymentAddress, | ||
| amountToPay, | ||
| `0x${paymentReference}`, | ||
| feeToPay, | ||
| feeAddress, | ||
| ]), | ||
| ]; | ||
|
|
||
| return { | ||
| amountToPay: totalAmount.toBigInt(), | ||
| tokenAddress, | ||
| ops, | ||
| }; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.