- 
                Notifications
    You must be signed in to change notification settings 
- Fork 75
feat: update spokepool relayer refund to handle blocked transfers #675
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
feat: update spokepool relayer refund to handle blocked transfers #675
Conversation
        
          
                contracts/SpokePool.sol
              
                Outdated
          
        
      | uint256 amount = refundAmounts[i]; | ||
| if (amount > 0) | ||
| IERC20Upgradeable(l2TokenAddressParsed).safeTransfer(refundAddresses[i].toAddress(), amount); | ||
| for (uint256 i = 0; i < refundAmounts.length; ++i) { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: we can cache refundAmounts.length by storing it in memory
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I cant add any extra state/memory variables due to yul issues beyond what I have now.
        
          
                contracts/SpokePool.sol
              
                Outdated
          
        
      | refundAmounts[i] | ||
| ) | ||
| returns (bool result) { | ||
| success = result; | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we also check that the balance has decreased as expected before considering it successful?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why? if the transfer succeeded it will be set as the bool returned from the call. if it fails it either reverts or returns false.
| // chain-specific bridging method. | ||
| if (amountToReturn > 0) { | ||
| _bridgeTokensToHubPool(amountToReturn, l2TokenAddressParsed); | ||
| _bridgeTokensToHubPool(amountToReturn, l2TokenAddress.toAddress()); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to check here that amountToReturn <= erc20.balanceOf(address(this)) because this case would be handled by the revert?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup.
| bytes message | ||
| ); | ||
|  | ||
| event ClaimedRelayerRefund( | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ordering is different than in SVM. This also has the caller added. Do you think we later update SVM to match this EVM interface?
        
          
                contracts/SpokePool.sol
              
                Outdated
          
        
      | for (uint256 i = 0; i < refundAmounts.length; ++i) { | ||
| totalRefundedAmount += refundAmounts[i]; | ||
|  | ||
| if (totalRefundedAmount > spokeStartBalance) revert InsufficientSpokePoolBalanceToExecuteLeaf(); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall we also later add this check to SVM?
        
          
                contracts/SpokePool.sol
              
                Outdated
          
        
      | refundAddresses[i].toAddress(), | ||
| refundAmounts[i] | ||
| ) | ||
| returns (bool result) { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might fail on successful transfers for tokens that do not return bool on success (e.g. USDT). So instead of try/catch pattern we might need to reimplement OZ safeTransfer without bubbling up the reverts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup, I agree. we should just catch on reverts. what do you think of this implementation?
else, you're right: we can implement this kind of structure internally
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the safe erc20 assumes non-reverting calls are assumed to be successful, which is what the new implementation now assumes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IERC20Upgradeable expects bool to be returned from the transfer, so USDT would always hit the catch block. Since try/catch works only on external calls or contract creation, the only viable option is to replicate the logic of OZ _callOptionalReturn and replacing all reverts with returning bool that we can track in our logic.
        
          
                contracts/SpokePool.sol
              
                Outdated
          
        
      | // Re-implementation of OZ _callOptionalReturnBool to use private logic. Function executes a transfer and returns a | ||
| // bool indicating if the external call was successful, rather than reverting. Original method: | ||
| // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol#L188 | ||
| function noRevertTransfer( | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: use underscore in internal methods. Or alt, move this to library
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Only a few minor comments
| address token, | ||
| address to, | ||
| uint256 amount | ||
| ) internal returns (bool) { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1, this function is exactly what we need to work with old tokens, like USDT.
| // to eliminate any chance of collision between pre and post V3 relay hashes. | ||
| mapping(bytes32 => uint256) public fillStatuses; | ||
|  | ||
| mapping(address => mapping(address => uint256)) public relayerRefund; | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sure you decrement the __gap array size at the bottom of the file to keep storage aligned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just decrement by 1 for a 2d mapping? I'm not totally sure how this gap variable works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spoke implementations for each chain inherit this SpokePool, which is also upgradable, so we need to ensure the storage layout on derived contracts stays the same
| uint256 returnSize; | ||
| uint256 returnValue; | ||
| bytes memory data = abi.encodeCall(IERC20Upgradeable.transfer, (to, amount)); | ||
| assembly { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be annotated as memory-safe, like in OZ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the solidity version we are using does not support this. it gave me an error when trying to do that.
Co-authored-by: Matt Rice <matthewcrice32@gmail.com>
Co-authored-by: Matt Rice <matthewcrice32@gmail.com>
….com:across-protocol/contracts into chrismaree/evm-execute-relayer-refund-update
        
          
                contracts/SpokePool.sol
              
                Outdated
          
        
      | */ | ||
| function claimRelayerRefund(address l2TokenAddress, address refundAddress) public { | ||
| uint256 liability = relayerRefund[l2TokenAddress][msg.sender]; | ||
| require(liability > 0, "No liability to claim"); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: rest of the contract uses if/revert pattern instead of reverts. Also in other places we use custom errors
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we use require syntax in many other files but you're right that in this file we don't. I've updated it accordingly.
* feat(chain-adapters): add solana adapter (#641) * feat(chain-adapters): add solana adapter Signed-off-by: Reinis Martinsons <reinis@umaproject.org> * fix: comments Signed-off-by: Reinis Martinsons <reinis@umaproject.org> * test: solana adapter Signed-off-by: Reinis Martinsons <reinis@umaproject.org> * Update contracts/chain-adapters/Solana_Adapter.sol Co-authored-by: Chris Maree <christopher.maree@gmail.com> * fix: do not hash bytes32 svm address Signed-off-by: Reinis Martinsons <reinis@umaproject.org> --------- Signed-off-by: Reinis Martinsons <reinis@umaproject.org> Co-authored-by: Chris Maree <christopher.maree@gmail.com> * feat: address to bytes32 contract changes (#650) * feat: add address to bytes32 contract changes Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * refactor: remove todos Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * refactor: imports Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * Update contracts/SpokePool.sol Co-authored-by: Reinis Martinsons <77973553+Reinis-FRP@users.noreply.github.com> * feat: bytes 32 comparisons Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * refactor: format code Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * fix: tests Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * feat: bytes 32 check Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * fix: ts Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * feat: reuse lib in cctp adapter Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * feat: _preExecuteLeafHook Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * refactor: comments Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * feat: _verifyUpdateV3DepositMessage Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * feat: backward compatibility Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * feat: backwards compatibility tests Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * feat: change comparison casting address bytes32 Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * fix: test Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * feat: merkle tree leaf to bytes32 Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * test: leaf type update fixes Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * feat: remove helper Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> --------- Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> Co-authored-by: Reinis Martinsons <77973553+Reinis-FRP@users.noreply.github.com> * feat: Add relayer repayment address (#653) * WIP Signed-off-by: chrismaree <christopher.maree@gmail.com> * WIP Signed-off-by: chrismaree <christopher.maree@gmail.com> * WIP Signed-off-by: chrismaree <christopher.maree@gmail.com> * WIP Signed-off-by: chrismaree <christopher.maree@gmail.com> * WIP Signed-off-by: chrismaree <christopher.maree@gmail.com> --------- Signed-off-by: chrismaree <christopher.maree@gmail.com> * fix: clean up cast utilities (#676) * WIP Signed-off-by: chrismaree <christopher.maree@gmail.com> * WIP Signed-off-by: chrismaree <christopher.maree@gmail.com> * WIP Signed-off-by: chrismaree <christopher.maree@gmail.com> * WIP Signed-off-by: chrismaree <christopher.maree@gmail.com> * WIP Signed-off-by: chrismaree <christopher.maree@gmail.com> * WIP Signed-off-by: chrismaree <christopher.maree@gmail.com> --------- Signed-off-by: chrismaree <christopher.maree@gmail.com> * feat: update spokepool relayer refund to handle blocked transfers (#675) Co-authored-by: Matt Rice <matthewcrice32@gmail.com> * WIP Signed-off-by: chrismaree <christopher.maree@gmail.com> * fix(evm): merkle tree tests bytes32 Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * WIP Signed-off-by: chrismaree <christopher.maree@gmail.com> * feat(svm): svm-dev fixes from review (#727) * refactor(svm): reuse bytes32 to address lib in svm adapter Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * feat: custom errors Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * feat: fix test Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> --------- Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * test: fix forge tests Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * proposal: ensure that EVM errors are always consistant on underflows (#720) * feat: revert bytes32 conversion for internal functions (#755) * Discard changes to contracts/Ovm_SpokePool.sol * fix: stack too deep (#766) * WIP Signed-off-by: chrismaree <christopher.maree@gmail.com> * WIP Signed-off-by: chrismaree <christopher.maree@gmail.com> * Revert "feat: update depositor to bytes32" (#764) This reverts commit 85f0001. * Discard changes to contracts/PolygonZkEVM_SpokePool.sol * Discard changes to contracts/Polygon_SpokePool.sol * fix: make event case consistant between evm & svm (#760) * feat(SpokePool): Remove depositExclusive (#642) This function was used to express exclusivity as a period but its no longer useful since depositV3 now allows caller to express exclusivityPeriod instead of exclusivityDeadline * feat: Introduce opt-in deterministic relay data hashes (again) (#639) * Revert "feat(SpokePool): Introduce opt-in deterministic relay data hashes (#583)" This reverts commit 9d21d1b. * Reapply "feat(SpokePool): Introduce opt-in deterministic relay data hashes (#583)" This reverts commit d363bf0. * add deposit nonces to 7683 Signed-off-by: Matt Rice <matthewcrice32@gmail.com> * fix Signed-off-by: Matt Rice <matthewcrice32@gmail.com> * WIP Signed-off-by: Matt Rice <matthewcrice32@gmail.com> * feat(SpokePool): Introduce opt-in deterministic relay data hashes (#583) * fix(SpokePool): Apply exclusivity consistently The new relative exclusivity check has not been propagated to fillV3RelayWithUpdatedDeposit(). Identified via test case failures in the relayer. Signed-off-by: Paul <108695806+pxrl@users.noreply.github.com> * Also check on slow fill requests * Update contracts/SpokePool.sol * lint * Update * Add pure * Fix * Add tests * improve(SpokePool): _depositV3 interprets `exclusivityParameter` as 0, an offset, or a timestamp There should be a way for the deposit transaction to remove chain re-org risk affecting the block.timestamp by allowing the caller to set a fixed `exclusivityDeadline` value. This supports the existing behavior where the `exclusivityDeadline` is always emitted as its passed in. The new behavior is that if the `exclusivityParameter`, which replaces the `exclusivityDeadlineOffset` parameter, is 0 or greater than 1 year in seconds, then the `exclusivityDeadline` is equal to this parameter. Otherwise, its interpreted by `_depositV3()` as an offset. The offset would be useful in cases where the origin chain will not re-org, for example. * Update SpokePool.sol * Update SpokePool.Relay.ts * Update SpokePool.SlowRelay.ts * Update contracts/SpokePool.sol Co-authored-by: Paul <108695806+pxrl@users.noreply.github.com> * Update SpokePool.sol * Update contracts/SpokePool.sol * rebase * Update SpokePool.sol * Revert "Merge branch 'npai/exclusivity-switch' into mrice32/deterministic-new" This reverts commit 2432944, reversing changes made to 6fe3534. * Revert "Merge branch 'npai/exclusivity-switch' into mrice32/deterministic-new" This reverts commit 2432944, reversing changes made to 6fe3534. * revert * Update SpokePool.sol * Fix * Update SpokePool.sol Co-authored-by: Chris Maree <christopher.maree@gmail.com> * WIP * WIP * wip * Update SpokePool.Relay.ts * Fix * Update SpokePool.sol * Update SpokePool.sol --------- Signed-off-by: Matt Rice <matthewcrice32@gmail.com> Signed-off-by: Paul <108695806+pxrl@users.noreply.github.com> Co-authored-by: nicholaspai <npai.nyc@gmail.com> Co-authored-by: nicholaspai <9457025+nicholaspai@users.noreply.github.com> Co-authored-by: Paul <108695806+pxrl@users.noreply.github.com> Co-authored-by: Chris Maree <christopher.maree@gmail.com> * docs: fix comment duplication (#775) Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> * fix: emit hashed message in evm fill events (#772) * fix: emit hashed message in evm fill events Signed-off-by: Reinis Martinsons <reinis@umaproject.org> * WIP Signed-off-by: chrismaree <christopher.maree@gmail.com> * fix: linting Signed-off-by: Reinis Martinsons <reinis@umaproject.org> --------- Signed-off-by: Reinis Martinsons <reinis@umaproject.org> Signed-off-by: chrismaree <christopher.maree@gmail.com> Co-authored-by: chrismaree <christopher.maree@gmail.com> * fix: linting Signed-off-by: Reinis Martinsons <reinis@umaproject.org> * feat: improve _getV3RelayHash method (#779) * WIP Signed-off-by: chrismaree <christopher.maree@gmail.com> * WIP Signed-off-by: chrismaree <christopher.maree@gmail.com> * fix: Address Storage layout issue in CI (#836) * add new storage layout Signed-off-by: Chris Maree <christopher.maree@gmail.com> * Discard changes to storage-layouts/PolygonZkEVM_SpokePool.json * Discard changes to storage-layouts/Redstone_SpokePool.json * Discard changes to storage-layouts/Scroll_SpokePool.json * Discard changes to storage-layouts/Zora_SpokePool.json * Discard changes to storage-layouts/WorldChain_SpokePool.json * add new storage layout Signed-off-by: Chris Maree <christopher.maree@gmail.com> --------- Signed-off-by: Chris Maree <christopher.maree@gmail.com> * fix(evm): C01 - Address incorrect use of relayerRefund over msg.sender in claimRelayerRefund function (#826) Signed-off-by: Chris Maree <christopher.maree@gmail.com> * fix(evm): L01 - Update function from public to external (#827) Signed-off-by: Chris Maree <christopher.maree@gmail.com> * fix(evm): L03 - Address incorrect Right Shift in AddressConverters Lib (#828) Signed-off-by: Chris Maree <christopher.maree@gmail.com> * fix(evm): L04 - Remove repeated function (#829) Signed-off-by: Chris Maree <christopher.maree@gmail.com> * fix(evm): N01 - Add missing docstring for repaymentAddress (#830) Signed-off-by: Chris Maree <christopher.maree@gmail.com> * fix(evm): N02 - Address typographical Errors in spoke pool (#831) * WIP Signed-off-by: Chris Maree <christopher.maree@gmail.com> * Update contracts/SpokePool.sol --------- Signed-off-by: Chris Maree <christopher.maree@gmail.com> Co-authored-by: Matt Rice <matthewcrice32@gmail.com> * feat: update function and event naming for backwards compatibility (#805) * WIP Signed-off-by: Chris Maree <chris@mac.speedport.ip> * WIP Signed-off-by: Chris Maree <chris@mac.speedport.ip> * WIP Signed-off-by: Chris Maree <christopher.maree@gmail.com> * WIP Signed-off-by: Chris Maree <christopher.maree@gmail.com> * refined overfloaded function structure Signed-off-by: Chris Maree <christopher.maree@gmail.com> * Discard changes to test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts * WIP Signed-off-by: Chris Maree <christopher.maree@gmail.com> * WIP Signed-off-by: Chris Maree <christopher.maree@gmail.com> * WIP Signed-off-by: Chris Maree <christopher.maree@gmail.com> * WIP Signed-off-by: Chris Maree <christopher.maree@gmail.com> * WIP Signed-off-by: Matt Rice <matthewcrice32@gmail.com> * WIP Signed-off-by: Matt Rice <matthewcrice32@gmail.com> * WIP Signed-off-by: Matt Rice <matthewcrice32@gmail.com> * WIP Signed-off-by: Matt Rice <matthewcrice32@gmail.com> * update event names Signed-off-by: Matt Rice <matthewcrice32@gmail.com> * fix tests Signed-off-by: Matt Rice <matthewcrice32@gmail.com> * update function Signed-off-by: Matt Rice <matthewcrice32@gmail.com> * update naming Signed-off-by: Matt Rice <matthewcrice32@gmail.com> * drop unintended svm changes Signed-off-by: Matt Rice <matthewcrice32@gmail.com> * WIP Signed-off-by: Chris Maree <christopher.maree@gmail.com> * WIP Signed-off-by: Chris Maree <christopher.maree@gmail.com> * WIP Signed-off-by: Chris Maree <christopher.maree@gmail.com> * feat: extend current add-legacy-fill-method-svm-dev (#864) * WIP Signed-off-by: Chris Maree <christopher.maree@gmail.com> --------- Signed-off-by: Chris Maree <chris@mac.speedport.ip> Signed-off-by: Chris Maree <christopher.maree@gmail.com> Signed-off-by: Matt Rice <matthewcrice32@gmail.com> Co-authored-by: Chris Maree <chris@mac.speedport.ip> Co-authored-by: Matt Rice <matthewcrice32@gmail.com> * fix: update legacy FilledV3Relay event to match old event signature (#873) * fix: update legacy event to match old event signature Signed-off-by: Matt Rice <matthewcrice32@gmail.com> * WIP Signed-off-by: Matt Rice <matthewcrice32@gmail.com> * WIP Signed-off-by: Matt Rice <matthewcrice32@gmail.com> --------- Signed-off-by: Matt Rice <matthewcrice32@gmail.com> * fix: use entire message when calculating relay hash for evm chains (#867) * fix: hash entire message when calculating relay hash for evm chains Signed-off-by: bennett <bennett@umaproject.org> * make getV3RelayHash public Signed-off-by: bennett <bennett@umaproject.org> * update fixture with relay hash change Signed-off-by: bennett <bennett@umaproject.org> --------- Signed-off-by: bennett <bennett@umaproject.org> * feat(SpokePool): Permit historical fillDeadline on deposit (#870) * feat(SpokePool): Permit historical fillDeadline on deposit This removes a sharp edge for pre-fill deposits, where the deposit comes after the fill. Permitting a historical fillDeadline gives more flexibility to the relayer around when they submit the deposit on the origin chain. * fix test * restore test * Bump approvals * fix: add check to ensure depositor is a valid EVM address (#874) Signed-off-by: Matt Rice <matthewcrice32@gmail.com> * fix(evm): L02 _destinationSettler Can Return Zero Address (#834) * fix: L02 _destinationSettler Can Return Zero Address * updated implementation to be in internal function Signed-off-by: Chris Maree <christopher.maree@gmail.com> --------- Signed-off-by: Chris Maree <christopher.maree@gmail.com> Co-authored-by: Chris Maree <christopher.maree@gmail.com> Co-authored-by: nicholaspai <npai.nyc@gmail.com> * improve: Verify relay hashes are the same pre and post upgrade (#878) * fix: hash entire message when calculating relay hash for evm chains Signed-off-by: bennett <bennett@umaproject.org> * make getV3RelayHash public Signed-off-by: bennett <bennett@umaproject.org> * update fixture with relay hash change Signed-off-by: bennett <bennett@umaproject.org> * improve: Verify relay hashes are the same pre and post upgrade Adds a simple unit test to check that the same data hash is constructed * fix * Update test/evm/hardhat/MerkleLib.Proofs.ts * Update test/evm/hardhat/SpokePool.Relay.ts * Update SpokePool.Relay.ts --------- Signed-off-by: bennett <bennett@umaproject.org> Co-authored-by: bennett <bennett@umaproject.org> * Fix merge conflict that removed exclusivity parameter * Fix SwapAndBridge merge conflict * reorder stack variables Signed-off-by: bennett <bennett@umaproject.org> * export test functions Signed-off-by: bennett <bennett@umaproject.org> * bump package Signed-off-by: bennett <bennett@umaproject.org> * fix: simpler solution to stack too deep --------- Signed-off-by: Reinis Martinsons <reinis@umaproject.org> Signed-off-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> Signed-off-by: chrismaree <christopher.maree@gmail.com> Signed-off-by: Matt Rice <matthewcrice32@gmail.com> Signed-off-by: Paul <108695806+pxrl@users.noreply.github.com> Signed-off-by: Chris Maree <christopher.maree@gmail.com> Signed-off-by: Chris Maree <chris@mac.speedport.ip> Signed-off-by: bennett <bennett@umaproject.org> Co-authored-by: Reinis Martinsons <77973553+Reinis-FRP@users.noreply.github.com> Co-authored-by: Pablo Maldonado <pablomaldonadoturci@gmail.com> Co-authored-by: Matt Rice <matthewcrice32@gmail.com> Co-authored-by: nicholaspai <9457025+nicholaspai@users.noreply.github.com> Co-authored-by: nicholaspai <npai.nyc@gmail.com> Co-authored-by: Paul <108695806+pxrl@users.noreply.github.com> Co-authored-by: Reinis Martinsons <reinis@umaproject.org> Co-authored-by: Chris Maree <chris@mac.speedport.ip> Co-authored-by: bmzig <57361391+bmzig@users.noreply.github.com> Co-authored-by: bennett <bennett@umaproject.org>
No description provided.