From b07d94ee41b5490efd972ecc0c754f482157ad55 Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Wed, 18 Sep 2024 20:02:36 +0100 Subject: [PATCH 01/90] remove global solidity extension settings --- .vscode/settings.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 116a5a5174..b61cf6f526 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,11 +16,6 @@ "eslint.format.enable": true, "editorconfig.generateAuto": false, "files.trimTrailingWhitespace": true, - "solidity.packageDefaultDependenciesContractsDirectory": "contracts", - "solidity.packageDefaultDependenciesDirectory": "lib", - "solidity.compileUsingRemoteVersion": "v0.8.17+commit.8df45f5f", - "solidity.formatter": "prettier", - "solidity.defaultCompiler": "remote", "tailwindCSS.classAttributes": [ ".*class.*", ".*Class.*", From 6e41b296d5a51011a9ad5fb0705180f34003f178 Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Wed, 18 Sep 2024 20:04:54 +0100 Subject: [PATCH 02/90] use monorepo support in global workspace only --- .vscode/settings.json | 1 + packages/contracts-core/.vscode/settings.json | 3 +++ packages/contracts-rfq/.vscode/settings.json | 3 +++ packages/solidity-devops/.vscode/settings.json | 3 +++ 4 files changed, 10 insertions(+) create mode 100644 packages/contracts-core/.vscode/settings.json create mode 100644 packages/contracts-rfq/.vscode/settings.json create mode 100644 packages/solidity-devops/.vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json index b61cf6f526..82ad2456a1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,6 +16,7 @@ "eslint.format.enable": true, "editorconfig.generateAuto": false, "files.trimTrailingWhitespace": true, + "solidity.monoRepoSupport": true, "tailwindCSS.classAttributes": [ ".*class.*", ".*Class.*", diff --git a/packages/contracts-core/.vscode/settings.json b/packages/contracts-core/.vscode/settings.json new file mode 100644 index 0000000000..e430b639be --- /dev/null +++ b/packages/contracts-core/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "solidity.monoRepoSupport": false +} diff --git a/packages/contracts-rfq/.vscode/settings.json b/packages/contracts-rfq/.vscode/settings.json new file mode 100644 index 0000000000..e430b639be --- /dev/null +++ b/packages/contracts-rfq/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "solidity.monoRepoSupport": false +} diff --git a/packages/solidity-devops/.vscode/settings.json b/packages/solidity-devops/.vscode/settings.json new file mode 100644 index 0000000000..e430b639be --- /dev/null +++ b/packages/solidity-devops/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "solidity.monoRepoSupport": false +} From 9049299b8977d0c4e924f5169319eaa324e3b876 Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Wed, 18 Sep 2024 20:29:31 +0100 Subject: [PATCH 03/90] - use Solidity extension for formatting *.sol files - use `forge fmt` as formatter in Solidity extension --- .vscode/settings.json | 4 ++++ packages/contracts-core/.vscode/settings.json | 4 ++++ packages/contracts-rfq/.vscode/settings.json | 4 ++++ packages/solidity-devops/.vscode/settings.json | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 82ad2456a1..e754f0e0f5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,8 @@ { "editor.fontLigatures": "'calt', 'liga', 'ss01', 'ss02', 'ss03', 'ss04', 'ss05', 'ss06', 'ss07', 'ss08', 'ss09'", + "[solidity]": { + "editor.defaultFormatter": "JuanBlanco.solidity" + }, "[typescript]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, @@ -16,6 +19,7 @@ "eslint.format.enable": true, "editorconfig.generateAuto": false, "files.trimTrailingWhitespace": true, + "solidity.formatter": "forge", "solidity.monoRepoSupport": true, "tailwindCSS.classAttributes": [ ".*class.*", diff --git a/packages/contracts-core/.vscode/settings.json b/packages/contracts-core/.vscode/settings.json index e430b639be..9cab755557 100644 --- a/packages/contracts-core/.vscode/settings.json +++ b/packages/contracts-core/.vscode/settings.json @@ -1,3 +1,7 @@ { + "[solidity]": { + "editor.defaultFormatter": "JuanBlanco.solidity" + }, + "solidity.formatter": "forge", "solidity.monoRepoSupport": false } diff --git a/packages/contracts-rfq/.vscode/settings.json b/packages/contracts-rfq/.vscode/settings.json index e430b639be..9cab755557 100644 --- a/packages/contracts-rfq/.vscode/settings.json +++ b/packages/contracts-rfq/.vscode/settings.json @@ -1,3 +1,7 @@ { + "[solidity]": { + "editor.defaultFormatter": "JuanBlanco.solidity" + }, + "solidity.formatter": "forge", "solidity.monoRepoSupport": false } diff --git a/packages/solidity-devops/.vscode/settings.json b/packages/solidity-devops/.vscode/settings.json index e430b639be..9cab755557 100644 --- a/packages/solidity-devops/.vscode/settings.json +++ b/packages/solidity-devops/.vscode/settings.json @@ -1,3 +1,7 @@ { + "[solidity]": { + "editor.defaultFormatter": "JuanBlanco.solidity" + }, + "solidity.formatter": "forge", "solidity.monoRepoSupport": false } From 23f6c4c652743c5ca7a184ad730ce19af3600a9c Mon Sep 17 00:00:00 2001 From: parodime Date: Fri, 20 Sep 2024 16:43:19 -0400 Subject: [PATCH 04/90] feat(contracts-rfq): relay/prove/claim with different address [SLT-130] (#3138) * init. solidity ^. FbV2 relay/prove/claim overloads * +IFastBridgeV2, explicit address0 cast, func scope & inheritdoc fixes * pragma lock, contract relabel * feat: start scoping V2 tests * test: override relayer role scenarios, no longer enforced by V2 * test: finish the parity test * test: the management methods * test: dst chain scenarios * test: bridge * test: prove * test: claim * test: dispute * test: refund * test: bridge reverts * remove redundant extend. rearrange inherit list * revert 0.8.20 in favor of user (non-ws) setting --------- Co-authored-by: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> --- packages/contracts-rfq/contracts/Admin.sol | 2 +- .../contracts-rfq/contracts/FastBridgeV2.sol | 287 ++++++ .../contracts/interfaces/IAdmin.sol | 2 +- .../contracts/interfaces/IFastBridge.sol | 2 +- .../contracts/interfaces/IFastBridgeV2.sol | 23 + .../contracts/interfaces/IMulticallTarget.sol | 2 +- .../contracts-rfq/contracts/libs/Errors.sol | 2 +- .../contracts/libs/UniversalToken.sol | 2 +- .../contracts/utils/MulticallTarget.sol | 2 +- .../script/ConfigureFastBridge.s.sol | 2 +- .../script/DeployFastBridge.CREATE2.s.sol | 2 +- packages/contracts-rfq/test/FastBridge.t.sol | 16 +- .../contracts-rfq/test/FastBridgeV2.Dst.t.sol | 155 ++++ .../test/FastBridgeV2.Management.t.sol | 141 +++ .../test/FastBridgeV2.Parity.t.sol | 40 + .../test/FastBridgeV2.Src.ProtocolFees.t.sol | 25 + .../contracts-rfq/test/FastBridgeV2.Src.t.sol | 833 ++++++++++++++++++ .../contracts-rfq/test/FastBridgeV2.t.sol | 121 +++ 18 files changed, 1644 insertions(+), 15 deletions(-) create mode 100644 packages/contracts-rfq/contracts/FastBridgeV2.sol create mode 100644 packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol create mode 100644 packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol create mode 100644 packages/contracts-rfq/test/FastBridgeV2.Management.t.sol create mode 100644 packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol create mode 100644 packages/contracts-rfq/test/FastBridgeV2.Src.ProtocolFees.t.sol create mode 100644 packages/contracts-rfq/test/FastBridgeV2.Src.t.sol create mode 100644 packages/contracts-rfq/test/FastBridgeV2.t.sol diff --git a/packages/contracts-rfq/contracts/Admin.sol b/packages/contracts-rfq/contracts/Admin.sol index 881b5bea6d..ffb352b28a 100644 --- a/packages/contracts-rfq/contracts/Admin.sol +++ b/packages/contracts-rfq/contracts/Admin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; import {AccessControlEnumerable} from "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol"; diff --git a/packages/contracts-rfq/contracts/FastBridgeV2.sol b/packages/contracts-rfq/contracts/FastBridgeV2.sol new file mode 100644 index 0000000000..7f938f28f1 --- /dev/null +++ b/packages/contracts-rfq/contracts/FastBridgeV2.sol @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +import "./libs/Errors.sol"; +import {UniversalTokenLib} from "./libs/UniversalToken.sol"; + +import {Admin} from "./Admin.sol"; +import {IFastBridge} from "./interfaces/IFastBridge.sol"; +import {IFastBridgeV2} from "./interfaces/IFastBridgeV2.sol"; + +contract FastBridgeV2 is Admin, IFastBridgeV2 { + using SafeERC20 for IERC20; + using UniversalTokenLib for address; + + /// @notice Dispute period for relayed transactions + uint256 public constant DISPUTE_PERIOD = 30 minutes; + + /// @notice Delay for a transaction after which it could be permisionlessly refunded + uint256 public constant REFUND_DELAY = 7 days; + + /// @notice Minimum deadline period to relay a requested bridge transaction + uint256 public constant MIN_DEADLINE_PERIOD = 30 minutes; + + enum BridgeStatus { + NULL, // doesn't exist yet + REQUESTED, + RELAYER_PROVED, + RELAYER_CLAIMED, + REFUNDED + } + + /// @notice Status of the bridge tx on origin chain + mapping(bytes32 => BridgeStatus) public bridgeStatuses; + /// @notice Proof of relayed bridge tx on origin chain + mapping(bytes32 => BridgeProof) public bridgeProofs; + /// @notice Whether bridge has been relayed on destination chain + mapping(bytes32 => bool) public bridgeRelays; + + /// @dev to prevent replays + uint256 public nonce; + // @dev the block the contract was deployed at + uint256 public immutable deployBlock; + + constructor(address _owner) Admin(_owner) { + deployBlock = block.number; + } + + /// @notice Pulls a requested token from the user to the requested recipient. + /// @dev Be careful of re-entrancy issues when msg.value > 0 and recipient != address(this) + function _pullToken(address recipient, address token, uint256 amount) internal returns (uint256 amountPulled) { + if (token != UniversalTokenLib.ETH_ADDRESS) { + token.assertIsContract(); + // Record token balance before transfer + amountPulled = IERC20(token).balanceOf(recipient); + // Token needs to be pulled only if msg.value is zero + // This way user can specify WETH as the origin asset + IERC20(token).safeTransferFrom(msg.sender, recipient, amount); + // Use the difference between the recorded balance and the current balance as the amountPulled + amountPulled = IERC20(token).balanceOf(recipient) - amountPulled; + } else { + // Otherwise, we need to check that ETH amount matches msg.value + if (amount != msg.value) revert MsgValueIncorrect(); + // Transfer value to recipient if not this address + if (recipient != address(this)) token.universalTransfer(recipient, amount); + // We will forward msg.value in the external call later, if recipient is not this contract + amountPulled = msg.value; + } + } + + /// @inheritdoc IFastBridge + function getBridgeTransaction(bytes memory request) public pure returns (BridgeTransaction memory) { + return abi.decode(request, (BridgeTransaction)); + } + + /// @inheritdoc IFastBridge + function bridge(BridgeParams memory params) external payable { + // check bridge params + if (params.dstChainId == block.chainid) revert ChainIncorrect(); + if (params.originAmount == 0 || params.destAmount == 0) revert AmountIncorrect(); + if (params.originToken == address(0) || params.destToken == address(0)) revert ZeroAddress(); + if (params.deadline < block.timestamp + MIN_DEADLINE_PERIOD) revert DeadlineTooShort(); + + // transfer tokens to bridge contract + // @dev use returned originAmount in request in case of transfer fees + uint256 originAmount = _pullToken(address(this), params.originToken, params.originAmount); + + // track amount of origin token owed to protocol + uint256 originFeeAmount; + if (protocolFeeRate > 0) originFeeAmount = (originAmount * protocolFeeRate) / FEE_BPS; + originAmount -= originFeeAmount; // remove from amount used in request as not relevant for relayers + + // set status to requested + bytes memory request = abi.encode( + BridgeTransaction({ + originChainId: uint32(block.chainid), + destChainId: params.dstChainId, + originSender: params.sender, + destRecipient: params.to, + originToken: params.originToken, + destToken: params.destToken, + originAmount: originAmount, + destAmount: params.destAmount, + originFeeAmount: originFeeAmount, + sendChainGas: params.sendChainGas, + deadline: params.deadline, + nonce: nonce++ // increment nonce on every bridge + }) + ); + bytes32 transactionId = keccak256(request); + bridgeStatuses[transactionId] = BridgeStatus.REQUESTED; + + emit BridgeRequested( + transactionId, + params.sender, + request, + params.dstChainId, + params.originToken, + params.destToken, + originAmount, + params.destAmount, + params.sendChainGas + ); + } + + /// @inheritdoc IFastBridge + function relay(bytes memory request) external payable { + relay(request, msg.sender); + } + + /// @inheritdoc IFastBridgeV2 + function relay(bytes memory request, address relayer) public payable { + bytes32 transactionId = keccak256(request); + BridgeTransaction memory transaction = getBridgeTransaction(request); + if (transaction.destChainId != uint32(block.chainid)) revert ChainIncorrect(); + + // check haven't exceeded deadline for relay to happen + if (block.timestamp > transaction.deadline) revert DeadlineExceeded(); + + // mark bridge transaction as relayed + if (bridgeRelays[transactionId]) revert TransactionRelayed(); + bridgeRelays[transactionId] = true; + + // transfer tokens to recipient on destination chain and gas rebate if requested + address to = transaction.destRecipient; + address token = transaction.destToken; + uint256 amount = transaction.destAmount; + + uint256 rebate = chainGasAmount; + if (!transaction.sendChainGas) { + // forward erc20 + rebate = 0; + _pullToken(to, token, amount); + } else if (token == UniversalTokenLib.ETH_ADDRESS) { + // lump in gas rebate into amount in native gas token + _pullToken(to, token, amount + rebate); + } else { + // forward erc20 then forward gas rebate in native gas token + _pullToken(to, token, amount); + _pullToken(to, UniversalTokenLib.ETH_ADDRESS, rebate); + } + + emit BridgeRelayed( + transactionId, + relayer, + to, + transaction.originChainId, + transaction.originToken, + transaction.destToken, + transaction.originAmount, + transaction.destAmount, + rebate + ); + } + + /// @inheritdoc IFastBridge + function prove(bytes memory request, bytes32 destTxHash) external { + prove(request, destTxHash, msg.sender); + } + + /// @inheritdoc IFastBridgeV2 + function prove(bytes memory request, bytes32 destTxHash, address relayer) public onlyRole(RELAYER_ROLE) { + bytes32 transactionId = keccak256(request); + // update bridge tx status given proof provided + if (bridgeStatuses[transactionId] != BridgeStatus.REQUESTED) revert StatusIncorrect(); + bridgeStatuses[transactionId] = BridgeStatus.RELAYER_PROVED; + bridgeProofs[transactionId] = BridgeProof({timestamp: uint96(block.timestamp), relayer: relayer}); // overflow ok + + emit BridgeProofProvided(transactionId, relayer, destTxHash); + } + + /// @notice Calculates time since proof submitted + /// @dev proof.timestamp stores casted uint96(block.timestamp) block timestamps for gas optimization + /// _timeSince(proof) can accomodate rollover case when block.timestamp > type(uint96).max but + /// proof.timestamp < type(uint96).max via unchecked statement + /// @param proof The bridge proof + /// @return delta Time delta since proof submitted + function _timeSince(BridgeProof memory proof) internal view returns (uint256 delta) { + unchecked { + delta = uint96(block.timestamp) - proof.timestamp; + } + } + + /// @inheritdoc IFastBridge + function canClaim(bytes32 transactionId, address relayer) external view returns (bool) { + if (bridgeStatuses[transactionId] != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect(); + BridgeProof memory proof = bridgeProofs[transactionId]; + if (proof.relayer != relayer) revert SenderIncorrect(); + return _timeSince(proof) > DISPUTE_PERIOD; + } + + /// @inheritdoc IFastBridgeV2 + function claim(bytes memory request) external { + claim(request, address(0)); + } + + /// @inheritdoc IFastBridge + function claim(bytes memory request, address to) public { + bytes32 transactionId = keccak256(request); + BridgeTransaction memory transaction = getBridgeTransaction(request); + + // update bridge tx status if able to claim origin collateral + if (bridgeStatuses[transactionId] != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect(); + + BridgeProof memory proof = bridgeProofs[transactionId]; + + // if "to" is zero addr, permissionlessly send funds to proven relayer + if (to == address(0)) { + to = proof.relayer; + } else if (proof.relayer != msg.sender) { + revert SenderIncorrect(); + } + + if (_timeSince(proof) <= DISPUTE_PERIOD) revert DisputePeriodNotPassed(); + + bridgeStatuses[transactionId] = BridgeStatus.RELAYER_CLAIMED; + + // update protocol fees if origin fee amount exists + if (transaction.originFeeAmount > 0) protocolFees[transaction.originToken] += transaction.originFeeAmount; + + // transfer origin collateral less fee to specified address + address token = transaction.originToken; + uint256 amount = transaction.originAmount; + token.universalTransfer(to, amount); + + emit BridgeDepositClaimed(transactionId, proof.relayer, to, token, amount); + } + + /// @inheritdoc IFastBridge + function dispute(bytes32 transactionId) external onlyRole(GUARD_ROLE) { + if (bridgeStatuses[transactionId] != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect(); + if (_timeSince(bridgeProofs[transactionId]) > DISPUTE_PERIOD) revert DisputePeriodPassed(); + + // @dev relayer gets slashed effectively if dest relay has gone thru + bridgeStatuses[transactionId] = BridgeStatus.REQUESTED; + delete bridgeProofs[transactionId]; + + emit BridgeProofDisputed(transactionId, msg.sender); + } + + /// @inheritdoc IFastBridge + function refund(bytes memory request) external { + bytes32 transactionId = keccak256(request); + BridgeTransaction memory transaction = getBridgeTransaction(request); + + if (hasRole(REFUNDER_ROLE, msg.sender)) { + // Refunder can refund if deadline has passed + if (block.timestamp <= transaction.deadline) revert DeadlineNotExceeded(); + } else { + // Permissionless refund is allowed after REFUND_DELAY + if (block.timestamp <= transaction.deadline + REFUND_DELAY) revert DeadlineNotExceeded(); + } + + // set status to refunded if still in requested state + if (bridgeStatuses[transactionId] != BridgeStatus.REQUESTED) revert StatusIncorrect(); + bridgeStatuses[transactionId] = BridgeStatus.REFUNDED; + + // transfer origin collateral back to original sender + address to = transaction.originSender; + address token = transaction.originToken; + uint256 amount = transaction.originAmount + transaction.originFeeAmount; + token.universalTransfer(to, amount); + + emit BridgeDepositRefunded(transactionId, to, token, amount); + } +} diff --git a/packages/contracts-rfq/contracts/interfaces/IAdmin.sol b/packages/contracts-rfq/contracts/interfaces/IAdmin.sol index 10d866d499..c6f398191d 100644 --- a/packages/contracts-rfq/contracts/interfaces/IAdmin.sol +++ b/packages/contracts-rfq/contracts/interfaces/IAdmin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; interface IAdmin { // ============ Events ============ diff --git a/packages/contracts-rfq/contracts/interfaces/IFastBridge.sol b/packages/contracts-rfq/contracts/interfaces/IFastBridge.sol index 0176d557bb..b691dfb5b4 100644 --- a/packages/contracts-rfq/contracts/interfaces/IFastBridge.sol +++ b/packages/contracts-rfq/contracts/interfaces/IFastBridge.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; interface IFastBridge { struct BridgeTransaction { diff --git a/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol new file mode 100644 index 0000000000..979551919a --- /dev/null +++ b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {IFastBridge} from "./IFastBridge.sol"; + +interface IFastBridgeV2 is IFastBridge { + + /// @notice Relays destination side of bridge transaction by off-chain relayer + /// @param request The encoded bridge transaction to relay on destination chain + /// @param relayer The address of the relaying entity which should have control of the origin funds when claimed + function relay(bytes memory request, address relayer) external payable; + + /// @notice Provides proof on origin side that relayer provided funds on destination side of bridge transaction + /// @param request The encoded bridge transaction to prove on origin chain + /// @param destTxHash The destination tx hash proving bridge transaction was relayed + /// @param relayer The address of the relaying entity which should have control of the origin funds when claimed + function prove(bytes memory request, bytes32 destTxHash, address relayer) external; + + /// @notice Completes bridge transaction on origin chain by claiming originally deposited capital. Can only send funds to the relayer address on the proof. + /// @param request The encoded bridge transaction to claim on origin chain + function claim(bytes memory request) external; + +} diff --git a/packages/contracts-rfq/contracts/interfaces/IMulticallTarget.sol b/packages/contracts-rfq/contracts/interfaces/IMulticallTarget.sol index 1f48e59609..d112057ad6 100644 --- a/packages/contracts-rfq/contracts/interfaces/IMulticallTarget.sol +++ b/packages/contracts-rfq/contracts/interfaces/IMulticallTarget.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; /// @notice Interface for a contract that can be called multiple times by the same caller. Inspired by MulticallV3: /// https://github.com/mds1/multicall/blob/master/src/Multicall3.sol diff --git a/packages/contracts-rfq/contracts/libs/Errors.sol b/packages/contracts-rfq/contracts/libs/Errors.sol index f2efb94304..b165dd46d4 100644 --- a/packages/contracts-rfq/contracts/libs/Errors.sol +++ b/packages/contracts-rfq/contracts/libs/Errors.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; error DeadlineExceeded(); error DeadlineNotExceeded(); diff --git a/packages/contracts-rfq/contracts/libs/UniversalToken.sol b/packages/contracts-rfq/contracts/libs/UniversalToken.sol index 98e6de0325..c57bf141e0 100644 --- a/packages/contracts-rfq/contracts/libs/UniversalToken.sol +++ b/packages/contracts-rfq/contracts/libs/UniversalToken.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; import {TokenNotContract} from "./Errors.sol"; diff --git a/packages/contracts-rfq/contracts/utils/MulticallTarget.sol b/packages/contracts-rfq/contracts/utils/MulticallTarget.sol index bed9266c33..d259b6c3db 100644 --- a/packages/contracts-rfq/contracts/utils/MulticallTarget.sol +++ b/packages/contracts-rfq/contracts/utils/MulticallTarget.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; import {IMulticallTarget} from "../interfaces/IMulticallTarget.sol"; diff --git a/packages/contracts-rfq/script/ConfigureFastBridge.s.sol b/packages/contracts-rfq/script/ConfigureFastBridge.s.sol index 4349fae615..4be61d98b1 100644 --- a/packages/contracts-rfq/script/ConfigureFastBridge.s.sol +++ b/packages/contracts-rfq/script/ConfigureFastBridge.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; import {FastBridge} from "../contracts/FastBridge.sol"; diff --git a/packages/contracts-rfq/script/DeployFastBridge.CREATE2.s.sol b/packages/contracts-rfq/script/DeployFastBridge.CREATE2.s.sol index 3369914b69..5d42a636b3 100644 --- a/packages/contracts-rfq/script/DeployFastBridge.CREATE2.s.sol +++ b/packages/contracts-rfq/script/DeployFastBridge.CREATE2.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; import {FastBridge} from "../contracts/FastBridge.sol"; diff --git a/packages/contracts-rfq/test/FastBridge.t.sol b/packages/contracts-rfq/test/FastBridge.t.sol index 264ebe3b79..2d90779c96 100644 --- a/packages/contracts-rfq/test/FastBridge.t.sol +++ b/packages/contracts-rfq/test/FastBridge.t.sol @@ -28,15 +28,19 @@ contract FastBridgeTest is Test { MockERC20 arbUSDC; MockERC20 ethUSDC; - function setUp() public { + function setUp() public virtual { vm.chainId(42_161); - fastBridge = new FastBridge(owner); + fastBridge = FastBridge(deployFastBridge()); arbUSDC = new MockERC20("arbUSDC", 6); ethUSDC = new MockERC20("ethUSDC", 6); _mintTokensToActors(); } - function _mintTokensToActors() internal { + function deployFastBridge() internal virtual returns (address) { + return address(new FastBridge(owner)); + } + + function _mintTokensToActors() internal virtual { arbUSDC.mint(relayer, 100 * 10 ** 6); arbUSDC.mint(guard, 100 * 10 ** 6); arbUSDC.mint(user, 100 * 10 ** 6); @@ -1297,7 +1301,7 @@ contract FastBridgeTest is Test { vm.stopPrank(); } - function test_failedRelayNotRelayer() public { + function test_failedRelayNotRelayer() public virtual { // Set up the roles for the test setUpRoles(); @@ -1642,7 +1646,7 @@ contract FastBridgeTest is Test { vm.stopPrank(); } - function test_failedClaimNotOldRelayer() public { + function test_failedClaimNotOldRelayer() public virtual { setUpRoles(); test_successfulBridge(); @@ -1679,7 +1683,7 @@ contract FastBridgeTest is Test { vm.stopPrank(); } - function test_failedClaimNotRelayer() public { + function test_failedClaimNotRelayer() public virtual { setUpRoles(); test_successfulRelayProof(); diff --git a/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol new file mode 100644 index 0000000000..926f05c8c0 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {ChainIncorrect, DeadlineExceeded, TransactionRelayed} from "../contracts/libs/Errors.sol"; + +import {FastBridgeV2, FastBridgeV2Test, IFastBridge} from "./FastBridgeV2.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2DstTest is FastBridgeV2Test { + event BridgeRelayed( + bytes32 indexed transactionId, + address indexed relayer, + address indexed to, + uint32 originChainId, + address originToken, + address destToken, + uint256 originAmount, + uint256 destAmount, + uint256 chainGasAmount + ); + + uint256 public constant LEFTOVER_BALANCE = 1 ether; + + function setUp() public override { + vm.chainId(DST_CHAIN_ID); + super.setUp(); + } + + function deployFastBridge() public override returns (FastBridgeV2) { + return new FastBridgeV2(address(this)); + } + + function mintTokens() public override { + dstToken.mint(address(relayerA), LEFTOVER_BALANCE + tokenParams.destAmount); + deal(relayerB, LEFTOVER_BALANCE + ethParams.destAmount); + vm.prank(relayerA); + dstToken.approve(address(fastBridge), type(uint256).max); + } + + function expectBridgeRelayed(IFastBridge.BridgeTransaction memory bridgeTx, bytes32 txId, address relayer) public { + vm.expectEmit(address(fastBridge)); + emit BridgeRelayed({ + transactionId: txId, + relayer: relayer, + to: bridgeTx.destRecipient, + originChainId: bridgeTx.originChainId, + originToken: bridgeTx.originToken, + destToken: bridgeTx.destToken, + originAmount: bridgeTx.originAmount, + destAmount: bridgeTx.destAmount, + chainGasAmount: 0 + }); + } + + function relay(address caller, uint256 msgValue, IFastBridge.BridgeTransaction memory bridgeTx) public { + bytes memory request = abi.encode(bridgeTx); + vm.prank(caller); + fastBridge.relay{value: msgValue}(request); + } + + function relayWithAddress( + address caller, + address relayer, + uint256 msgValue, + IFastBridge.BridgeTransaction memory bridgeTx + ) + public + { + bytes memory request = abi.encode(bridgeTx); + vm.prank(caller); + fastBridge.relay{value: msgValue}(request, relayer); + } + + /// @notice RelayerA completes the ERC20 bridge request + function test_relay_token() public { + bytes32 txId = getTxId(tokenTx); + expectBridgeRelayed(tokenTx, txId, address(relayerA)); + relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx}); + assertTrue(fastBridge.bridgeRelays(txId)); + assertEq(dstToken.balanceOf(address(userB)), tokenParams.destAmount); + assertEq(dstToken.balanceOf(address(relayerA)), LEFTOVER_BALANCE); + assertEq(dstToken.balanceOf(address(fastBridge)), 0); + } + + /// @notice RelayerA completes the ERC20 bridge request, using relayerB's address + function test_relay_token_withRelayerAddress() public { + bytes32 txId = getTxId(tokenTx); + expectBridgeRelayed(tokenTx, txId, address(relayerB)); + relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: 0, bridgeTx: tokenTx}); + assertTrue(fastBridge.bridgeRelays(txId)); + assertEq(dstToken.balanceOf(address(userB)), tokenParams.destAmount); + assertEq(dstToken.balanceOf(address(relayerA)), LEFTOVER_BALANCE); + assertEq(dstToken.balanceOf(address(fastBridge)), 0); + } + + /// @notice RelayerB completes the ETH bridge request + function test_relay_eth() public { + bytes32 txId = getTxId(ethTx); + expectBridgeRelayed(ethTx, txId, address(relayerB)); + relay({caller: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + assertTrue(fastBridge.bridgeRelays(txId)); + assertEq(address(userB).balance, ethParams.destAmount); + assertEq(address(relayerB).balance, LEFTOVER_BALANCE); + assertEq(address(fastBridge).balance, 0); + } + + /// @notice RelayerB completes the ETH bridge request, using relayerA's address + function test_relay_eth_withRelayerAddress() public { + bytes32 txId = getTxId(ethTx); + expectBridgeRelayed(ethTx, txId, address(relayerA)); + relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + assertTrue(fastBridge.bridgeRelays(txId)); + assertEq(address(userB).balance, ethParams.destAmount); + assertEq(address(relayerB).balance, LEFTOVER_BALANCE); + assertEq(address(fastBridge).balance, 0); + } + + // ══════════════════════════════════════════════════ REVERTS ══════════════════════════════════════════════════════ + + function test_relay_revert_chainIncorrect() public { + vm.chainId(SRC_CHAIN_ID); + vm.expectRevert(ChainIncorrect.selector); + relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_revert_transactionRelayed() public { + relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx}); + vm.expectRevert(TransactionRelayed.selector); + relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_revert_deadlineExceeded() public { + skip(DEADLINE + 1); + vm.expectRevert(DeadlineExceeded.selector); + relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_withRelayerAddress_revert_chainIncorrect() public { + vm.chainId(SRC_CHAIN_ID); + vm.expectRevert(ChainIncorrect.selector); + relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_withRelayerAddress_revert_transactionRelayed() public { + relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx}); + vm.expectRevert(TransactionRelayed.selector); + relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_withRelayerAddress_revert_deadlineExceeded() public { + skip(DEADLINE + 1); + vm.expectRevert(DeadlineExceeded.selector); + relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: 0, bridgeTx: tokenTx}); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Management.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Management.t.sol new file mode 100644 index 0000000000..58502618b9 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Management.t.sol @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2, FastBridgeV2Test} from "./FastBridgeV2.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2ManagementTest is FastBridgeV2Test { + uint256 public constant FEE_RATE_MAX = 1e4; // 1% + bytes32 public constant GOVERNOR_ROLE = keccak256("GOVERNOR_ROLE"); + + address public admin = makeAddr("Admin"); + address public governorA = makeAddr("Governor A"); + + event FeeRateUpdated(uint256 oldFeeRate, uint256 newFeeRate); + event FeesSwept(address token, address recipient, uint256 amount); + event ChainGasAmountUpdated(uint256 oldChainGasAmount, uint256 newChainGasAmount); + + function deployFastBridge() public override returns (FastBridgeV2) { + return new FastBridgeV2(admin); + } + + function configureFastBridge() public override { + setGovernor(admin, governor); + } + + function mintTokens() public override { + srcToken.mint(address(fastBridge), 100); + deal(address(fastBridge), 200); + cheatCollectedProtocolFees(address(srcToken), 100); + cheatCollectedProtocolFees(ETH_ADDRESS, 200); + } + + function setGovernor(address caller, address newGovernor) public { + vm.prank(caller); + fastBridge.grantRole(GOVERNOR_ROLE, newGovernor); + } + + function setProtocolFeeRate(address caller, uint256 newFeeRate) public { + vm.prank(caller); + fastBridge.setProtocolFeeRate(newFeeRate); + } + + function sweepProtocolFees(address caller, address token, address recipient) public { + vm.prank(caller); + fastBridge.sweepProtocolFees(token, recipient); + } + + function setChainGasAmount(address caller, uint256 newChainGasAmount) public { + vm.prank(caller); + fastBridge.setChainGasAmount(newChainGasAmount); + } + + function test_grantGovernorRole() public { + assertFalse(fastBridge.hasRole(GOVERNOR_ROLE, governorA)); + setGovernor(admin, governorA); + assertTrue(fastBridge.hasRole(GOVERNOR_ROLE, governorA)); + } + + function test_grantGovernorRole_revertNotAdmin(address caller) public { + vm.assume(caller != admin); + expectUnauthorized(caller, fastBridge.DEFAULT_ADMIN_ROLE()); + setGovernor(caller, governorA); + } + + // ═══════════════════════════════════════════ SET PROTOCOL FEE RATE ═══════════════════════════════════════════════ + + function test_setProtocolFeeRate() public { + vm.expectEmit(address(fastBridge)); + emit FeeRateUpdated(0, 123); + setProtocolFeeRate(governor, 123); + assertEq(fastBridge.protocolFeeRate(), 123); + } + + function test_setProtocolFeeRate_twice() public { + test_setProtocolFeeRate(); + vm.expectEmit(address(fastBridge)); + emit FeeRateUpdated(123, FEE_RATE_MAX); + setProtocolFeeRate(governor, FEE_RATE_MAX); + assertEq(fastBridge.protocolFeeRate(), FEE_RATE_MAX); + } + + function test_setProtocolFeeRate_revert_tooHigh() public { + vm.expectRevert("newFeeRate > max"); + setProtocolFeeRate(governor, FEE_RATE_MAX + 1); + } + + function test_setProtocolFeeRate_revert_notGovernor(address caller) public { + vm.assume(caller != governor); + expectUnauthorized(caller, fastBridge.GOVERNOR_ROLE()); + setProtocolFeeRate(caller, 123); + } + + // ════════════════════════════════════════════ SWEEP PROTOCOL FEES ════════════════════════════════════════════════ + + function test_sweepProtocolFees_erc20() public { + vm.expectEmit(address(fastBridge)); + emit FeesSwept(address(srcToken), governorA, 100); + sweepProtocolFees(governor, address(srcToken), governorA); + assertEq(srcToken.balanceOf(address(fastBridge)), 0); + assertEq(srcToken.balanceOf(governorA), 100); + assertEq(fastBridge.protocolFees(address(srcToken)), 0); + } + + function test_sweepProtocolFees_eth() public { + vm.expectEmit(address(fastBridge)); + emit FeesSwept(ETH_ADDRESS, governorA, 200); + sweepProtocolFees(governor, ETH_ADDRESS, governorA); + assertEq(address(fastBridge).balance, 0); + assertEq(governorA.balance, 200); + assertEq(fastBridge.protocolFees(ETH_ADDRESS), 0); + } + + function test_sweepProtocolFees_revertNotGovernor(address caller) public { + vm.assume(caller != governor); + expectUnauthorized(caller, fastBridge.GOVERNOR_ROLE()); + sweepProtocolFees(caller, address(srcToken), governorA); + } + + // ═══════════════════════════════════════════ SET CHAIN GAS AMOUNT ════════════════════════════════════════════════ + + function test_setChainGasAmount() public { + vm.expectEmit(address(fastBridge)); + emit ChainGasAmountUpdated(0, 123); + setChainGasAmount(governor, 123); + assertEq(fastBridge.chainGasAmount(), 123); + } + + function test_setChainGasAmount_twice() public { + test_setChainGasAmount(); + vm.expectEmit(address(fastBridge)); + emit ChainGasAmountUpdated(123, 456); + setChainGasAmount(governor, 456); + assertEq(fastBridge.chainGasAmount(), 456); + } + + function test_setChainGasAmount_revertNotGovernor(address caller) public { + vm.assume(caller != governor); + expectUnauthorized(caller, fastBridge.GOVERNOR_ROLE()); + setChainGasAmount(caller, 123); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol new file mode 100644 index 0000000000..d92e4f8ac5 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +import {FastBridgeTest, SenderIncorrect} from "./FastBridge.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2ParityTest is FastBridgeTest { + address public anotherRelayer = makeAddr("Another Relayer"); + + function deployFastBridge() internal virtual override returns (address) { + // Use the cheatcode to deploy 0.8.24 contract within a 0.8.20 test + return deployCode({what: "FastBridgeV2", args: abi.encode(owner)}); + } + + /// @notice Relay function is no longer permissioned, so we skip this test + function test_failedRelayNotRelayer() public virtual override { + vm.skip(true); + } + + /// @notice Claim function is no longer permissioned by the role (but still by proven address), + /// so we skip this test + function test_failedClaimNotRelayer() public virtual override { + vm.skip(true); + } + + /// @notice Claim function is no longer permissioned by the role (but still by proven address), + /// so we modify the parent test by removing the role assignment. + function test_failedClaimNotOldRelayer() public virtual override { + setUpRoles(); + test_successfulBridge(); + (bytes memory request,) = _getBridgeRequestAndId(block.chainid, 0, 0); + vm.warp(block.timestamp + 31 minutes); + vm.prank(relayer); + fastBridge.prove(request, bytes32("0x04")); + + vm.expectRevert(abi.encodeWithSelector(SenderIncorrect.selector)); + vm.prank(anotherRelayer); + fastBridge.claim(request, relayer); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.ProtocolFees.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.ProtocolFees.t.sol new file mode 100644 index 0000000000..d9a5324ed1 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.ProtocolFees.t.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2SrcTest} from "./FastBridgeV2.Src.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2SrcProtocolFeesTest is FastBridgeV2SrcTest { + function configureFastBridge() public virtual override { + super.configureFastBridge(); + fastBridge.grantRole(fastBridge.GOVERNOR_ROLE(), address(this)); + fastBridge.setProtocolFeeRate(1e4); // 1% + } + + function createFixtures() public virtual override { + super.createFixtures(); + tokenTx.originFeeAmount = 0.01e6; + tokenTx.originAmount = 0.99e6; + tokenTx.destAmount = 0.98e6; + tokenParams.destAmount = 0.98e6; + ethTx.originFeeAmount = 0.01 ether; + ethTx.originAmount = 0.99 ether; + ethTx.destAmount = 0.98 ether; + ethParams.destAmount = 0.98 ether; + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol new file mode 100644 index 0000000000..8a0106d0c0 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol @@ -0,0 +1,833 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import { + AmountIncorrect, + ChainIncorrect, + DisputePeriodNotPassed, + DisputePeriodPassed, + DeadlineNotExceeded, + DeadlineTooShort, + MsgValueIncorrect, + SenderIncorrect, + StatusIncorrect, + ZeroAddress +} from "../contracts/libs/Errors.sol"; + +import {FastBridgeV2, FastBridgeV2Test, IFastBridge} from "./FastBridgeV2.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2SrcTest is FastBridgeV2Test { + event BridgeRequested( + bytes32 indexed transactionId, + address indexed sender, + bytes request, + uint32 destChainId, + address originToken, + address destToken, + uint256 originAmount, + uint256 destAmount, + bool sendChainGas + ); + + event BridgeProofProvided(bytes32 indexed transactionId, address indexed relayer, bytes32 transactionHash); + + event BridgeDepositClaimed( + bytes32 indexed transactionId, address indexed relayer, address indexed to, address token, uint256 amount + ); + + event BridgeProofDisputed(bytes32 indexed transactionId, address indexed relayer); + + event BridgeDepositRefunded(bytes32 indexed transactionId, address indexed to, address token, uint256 amount); + + uint256 public constant MIN_DEADLINE = 30 minutes; + uint256 public constant CLAIM_DELAY = 30 minutes; + uint256 public constant PERMISSIONLESS_REFUND_DELAY = 7 days; + + uint256 public constant LEFTOVER_BALANCE = 1 ether; + uint256 public constant INITIAL_PROTOCOL_FEES_TOKEN = 456_789; + uint256 public constant INITIAL_PROTOCOL_FEES_ETH = 0.123 ether; + + address public claimTo = makeAddr("Claim To"); + + function setUp() public override { + vm.chainId(SRC_CHAIN_ID); + super.setUp(); + } + + function deployFastBridge() public override returns (FastBridgeV2) { + return new FastBridgeV2(address(this)); + } + + function configureFastBridge() public virtual override { + fastBridge.grantRole(fastBridge.RELAYER_ROLE(), relayerA); + fastBridge.grantRole(fastBridge.RELAYER_ROLE(), relayerB); + fastBridge.grantRole(fastBridge.GUARD_ROLE(), guard); + fastBridge.grantRole(fastBridge.REFUNDER_ROLE(), refunder); + } + + function mintTokens() public override { + // Prior Protocol fees + srcToken.mint(address(fastBridge), INITIAL_PROTOCOL_FEES_TOKEN); + deal(address(fastBridge), INITIAL_PROTOCOL_FEES_ETH); + cheatCollectedProtocolFees(address(srcToken), INITIAL_PROTOCOL_FEES_TOKEN); + cheatCollectedProtocolFees(ETH_ADDRESS, INITIAL_PROTOCOL_FEES_ETH); + // Users + srcToken.mint(userA, LEFTOVER_BALANCE + tokenParams.originAmount); + srcToken.mint(userB, LEFTOVER_BALANCE + tokenParams.originAmount); + deal(userA, LEFTOVER_BALANCE + ethParams.originAmount); + deal(userB, LEFTOVER_BALANCE + ethParams.originAmount); + vm.prank(userA); + srcToken.approve(address(fastBridge), type(uint256).max); + vm.prank(userB); + srcToken.approve(address(fastBridge), type(uint256).max); + } + + function bridge(address caller, uint256 msgValue, IFastBridge.BridgeParams memory params) public { + vm.prank(caller); + fastBridge.bridge{value: msgValue}(params); + } + + function prove(address caller, IFastBridge.BridgeTransaction memory bridgeTx, bytes32 destTxHash) public { + vm.prank(caller); + fastBridge.prove(abi.encode(bridgeTx), destTxHash); + } + + function claim(address caller, IFastBridge.BridgeTransaction memory bridgeTx) public { + vm.prank(caller); + fastBridge.claim(abi.encode(bridgeTx)); + } + + function claim(address caller, IFastBridge.BridgeTransaction memory bridgeTx, address to) public { + vm.prank(caller); + fastBridge.claim(abi.encode(bridgeTx), to); + } + + function dispute(address caller, bytes32 txId) public { + vm.prank(caller); + fastBridge.dispute(txId); + } + + function refund(address caller, IFastBridge.BridgeTransaction memory bridgeTx) public { + vm.prank(caller); + fastBridge.refund(abi.encode(bridgeTx)); + } + + function expectBridgeRequested(IFastBridge.BridgeTransaction memory bridgeTx, bytes32 txId) public { + vm.expectEmit(address(fastBridge)); + emit BridgeRequested({ + transactionId: txId, + sender: bridgeTx.originSender, + request: abi.encode(bridgeTx), + destChainId: bridgeTx.destChainId, + originToken: bridgeTx.originToken, + destToken: bridgeTx.destToken, + originAmount: bridgeTx.originAmount, + destAmount: bridgeTx.destAmount, + sendChainGas: bridgeTx.sendChainGas + }); + } + + function expectBridgeProofProvided(bytes32 txId, address relayer, bytes32 destTxHash) public { + vm.expectEmit(address(fastBridge)); + emit BridgeProofProvided({transactionId: txId, relayer: relayer, transactionHash: destTxHash}); + } + + function expectBridgeDepositClaimed( + IFastBridge.BridgeTransaction memory bridgeTx, + bytes32 txId, + address relayer, + address to + ) + public + { + vm.expectEmit(address(fastBridge)); + emit BridgeDepositClaimed({ + transactionId: txId, + relayer: relayer, + to: to, + token: bridgeTx.originToken, + amount: bridgeTx.originAmount + }); + } + + function expectBridgeProofDisputed(bytes32 txId, address guard) public { + vm.expectEmit(address(fastBridge)); + // Note: BridgeProofDisputed event has a mislabeled address parameter, this is actually the guard + emit BridgeProofDisputed({transactionId: txId, relayer: guard}); + } + + function expectBridgeDepositRefunded(IFastBridge.BridgeParams memory bridgeParams, bytes32 txId) public { + vm.expectEmit(address(fastBridge)); + emit BridgeDepositRefunded({ + transactionId: txId, + to: bridgeParams.sender, + token: bridgeParams.originToken, + amount: bridgeParams.originAmount + }); + } + + function assertEq(FastBridgeV2.BridgeStatus a, FastBridgeV2.BridgeStatus b) public pure { + assertEq(uint8(a), uint8(b)); + } + + // ══════════════════════════════════════════════════ BRIDGE ═══════════════════════════════════════════════════════ + + function checkTokenBalancesAfterBridge(address caller) public view { + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + assertEq(srcToken.balanceOf(caller), LEFTOVER_BALANCE); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + } + + function test_bridge_token() public { + bytes32 txId = getTxId(tokenTx); + expectBridgeRequested(tokenTx, txId); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + checkTokenBalancesAfterBridge(userA); + } + + function test_bridge_token_diffSender() public { + bytes32 txId = getTxId(tokenTx); + expectBridgeRequested(tokenTx, txId); + bridge({caller: userB, msgValue: 0, params: tokenParams}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + tokenParams.originAmount); + checkTokenBalancesAfterBridge(userB); + } + + function checkEthBalancesAfterBridge(address caller) public view { + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); + assertEq(address(caller).balance, LEFTOVER_BALANCE); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethParams.originAmount); + } + + function test_bridge_eth() public { + // bridge token first to match the nonce + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + expectBridgeRequested(ethTx, txId); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + checkEthBalancesAfterBridge(userA); + } + + function test_bridge_eth_diffSender() public { + // bridge token first to match the nonce + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + expectBridgeRequested(ethTx, txId); + bridge({caller: userB, msgValue: ethParams.originAmount, params: ethParams}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(userA.balance, LEFTOVER_BALANCE + ethParams.originAmount); + checkEthBalancesAfterBridge(userB); + } + + function test_bridge_userSpecificNonce() public { + vm.skip(true); // TODO: unskip when implemented + bridge({caller: userA, msgValue: 0, params: tokenParams}); + // UserB nonce is 0 + ethTx.nonce = 0; + ethParams.sender = userB; + ethTx.originSender = userB; + bytes32 txId = getTxId(ethTx); + expectBridgeRequested(ethTx, txId); + bridge({caller: userB, msgValue: ethParams.originAmount, params: ethParams}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + checkEthBalancesAfterBridge(userB); + } + + function test_bridge_eth_revert_lowerMsgValue() public { + vm.expectRevert(MsgValueIncorrect.selector); + bridge({caller: userA, msgValue: ethParams.originAmount - 1, params: ethParams}); + } + + function test_bridge_eth_revert_higherMsgValue() public { + vm.expectRevert(MsgValueIncorrect.selector); + bridge({caller: userA, msgValue: ethParams.originAmount + 1, params: ethParams}); + } + + function test_bridge_eth_revert_zeroMsgValue() public { + vm.expectRevert(MsgValueIncorrect.selector); + bridge({caller: userA, msgValue: 0, params: ethParams}); + } + + function test_bridge_revert_sameDestinationChain() public { + tokenParams.dstChainId = SRC_CHAIN_ID; + vm.expectRevert(ChainIncorrect.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + } + + function test_bridge_revert_zeroOriginAmount() public { + tokenParams.originAmount = 0; + vm.expectRevert(AmountIncorrect.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + } + + function test_bridge_revert_zeroDestAmount() public { + tokenParams.destAmount = 0; + vm.expectRevert(AmountIncorrect.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + } + + function test_bridge_revert_zeroOriginToken() public { + tokenParams.originToken = address(0); + vm.expectRevert(ZeroAddress.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + } + + function test_bridge_revert_zeroDestToken() public { + tokenParams.destToken = address(0); + vm.expectRevert(ZeroAddress.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + } + + function test_bridge_revert_zeroSender() public { + vm.skip(true); // TODO: unskip when fixed + tokenParams.sender = address(0); + vm.expectRevert(ZeroAddress.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + } + + function test_bridge_revert_zeroRecipient() public { + vm.skip(true); // TODO: unskip when fixed + tokenParams.to = address(0); + vm.expectRevert(ZeroAddress.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + } + + function test_bridge_revert_deadlineTooClose() public { + tokenParams.deadline = block.timestamp + MIN_DEADLINE - 1; + vm.expectRevert(DeadlineTooShort.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + } + + // ═══════════════════════════════════════════════════ PROVE ═══════════════════════════════════════════════════════ + + function test_prove_token() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + function test_prove_eth() public { + // bridge token first to match the nonce + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"}); + prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethParams.originAmount); + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); + } + + function test_prove_revert_statusNull() public { + vm.expectRevert(StatusIncorrect.selector); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + } + + function test_prove_revert_statusProved() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + vm.expectRevert(StatusIncorrect.selector); + prove({caller: relayerB, bridgeTx: tokenTx, destTxHash: hex"02"}); + } + + function test_prove_revert_statusClaimed() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + vm.expectRevert(StatusIncorrect.selector); + prove({caller: relayerB, bridgeTx: tokenTx, destTxHash: hex"02"}); + } + + function test_prove_revert_statusRefunded() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE + 1); + refund({caller: refunder, bridgeTx: tokenTx}); + vm.expectRevert(StatusIncorrect.selector); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + } + + function test_prove_revert_callerNotRelayer(address caller) public { + vm.assume(caller != relayerA && caller != relayerB); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + expectUnauthorized(caller, fastBridge.RELAYER_ROLE()); + prove({caller: caller, bridgeTx: tokenTx, destTxHash: hex"01"}); + } + + // ═══════════════════════════════════════════════════ CLAIM ═══════════════════════════════════════════════════════ + + function checkTokenBalancesAfterClaim(address relayer) public view { + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN + tokenTx.originFeeAmount); + assertEq(srcToken.balanceOf(relayer), tokenTx.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenTx.originFeeAmount); + } + + function test_claim_token() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + assertTrue(fastBridge.canClaim(txId, relayerA)); + expectBridgeDepositClaimed({bridgeTx: tokenTx, txId: txId, relayer: relayerA, to: relayerA}); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + checkTokenBalancesAfterClaim(relayerA); + } + + function test_claim_token_permissionless(address caller) public { + vm.assume(caller != relayerA); + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + expectBridgeDepositClaimed({bridgeTx: tokenTx, txId: txId, relayer: relayerA, to: relayerA}); + claim({caller: caller, bridgeTx: tokenTx}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + checkTokenBalancesAfterClaim(relayerA); + } + + function test_claim_token_permissionless_toZeroAddress(address caller) public { + vm.assume(caller != relayerA); + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + expectBridgeDepositClaimed({bridgeTx: tokenTx, txId: txId, relayer: relayerA, to: relayerA}); + claim({caller: caller, bridgeTx: tokenTx, to: address(0)}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + checkTokenBalancesAfterClaim(relayerA); + } + + function test_claim_token_toDiffAddress() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + expectBridgeDepositClaimed({bridgeTx: tokenTx, txId: txId, relayer: relayerA, to: claimTo}); + claim({caller: relayerA, bridgeTx: tokenTx, to: claimTo}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(srcToken.balanceOf(relayerA), 0); + checkTokenBalancesAfterClaim(claimTo); + } + + function test_claim_token_longDelay() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 30 days); + expectBridgeDepositClaimed({bridgeTx: tokenTx, txId: txId, relayer: relayerA, to: relayerA}); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + checkTokenBalancesAfterClaim(relayerA); + } + + function checkEthBalancesAfterClaim(address relayer) public view { + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH + ethTx.originFeeAmount); + assertEq(address(relayer).balance, ethTx.originAmount); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethTx.originFeeAmount); + } + + function test_claim_eth() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + assertTrue(fastBridge.canClaim(txId, relayerA)); + expectBridgeDepositClaimed({bridgeTx: ethTx, txId: txId, relayer: relayerA, to: relayerA}); + claim({caller: relayerA, bridgeTx: ethTx, to: relayerA}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + checkEthBalancesAfterClaim(relayerA); + } + + function test_claim_eth_permissionless(address caller) public { + vm.assume(caller != relayerA); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + expectBridgeDepositClaimed({bridgeTx: ethTx, txId: txId, relayer: relayerA, to: relayerA}); + claim({caller: caller, bridgeTx: ethTx}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + checkEthBalancesAfterClaim(relayerA); + } + + function test_claim_eth_permissionless_toZeroAddress(address caller) public { + vm.assume(caller != relayerA); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + expectBridgeDepositClaimed({bridgeTx: ethTx, txId: txId, relayer: relayerA, to: relayerA}); + claim({caller: caller, bridgeTx: ethTx, to: address(0)}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + checkEthBalancesAfterClaim(relayerA); + } + + function test_claim_eth_toDiffAddress() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + expectBridgeDepositClaimed({bridgeTx: ethTx, txId: txId, relayer: relayerA, to: claimTo}); + claim({caller: relayerA, bridgeTx: ethTx, to: claimTo}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + checkEthBalancesAfterClaim(claimTo); + } + + function test_claim_eth_longDelay() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 30 days); + expectBridgeDepositClaimed({bridgeTx: ethTx, txId: txId, relayer: relayerA, to: relayerA}); + claim({caller: relayerA, bridgeTx: ethTx, to: relayerA}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + checkEthBalancesAfterClaim(relayerA); + } + + function test_claim_revert_zeroDelay() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + assertFalse(fastBridge.canClaim(getTxId(tokenTx), relayerA)); + vm.expectRevert(DisputePeriodNotPassed.selector); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + } + + function test_claim_revert_smallerDelay() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY); + assertFalse(fastBridge.canClaim(getTxId(tokenTx), relayerA)); + vm.expectRevert(DisputePeriodNotPassed.selector); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + } + + function test_claim_revert_callerNotProven(address caller, address to) public { + vm.assume(caller != relayerA && to != address(0)); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + vm.expectRevert(SenderIncorrect.selector); + fastBridge.canClaim(getTxId(tokenTx), caller); + vm.expectRevert(SenderIncorrect.selector); + claim({caller: caller, bridgeTx: tokenTx, to: to}); + } + + function test_claim_revert_statusNull() public { + bytes32 txId = getTxId(tokenTx); + vm.expectRevert(StatusIncorrect.selector); + fastBridge.canClaim(txId, relayerA); + vm.expectRevert(StatusIncorrect.selector); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + } + + function test_claim_revert_statusRequested() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + vm.expectRevert(StatusIncorrect.selector); + fastBridge.canClaim(txId, relayerA); + vm.expectRevert(StatusIncorrect.selector); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + } + + function test_claim_revert_statusClaimed() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + vm.expectRevert(StatusIncorrect.selector); + fastBridge.canClaim(txId, relayerA); + vm.expectRevert(StatusIncorrect.selector); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + } + + function test_claim_revert_statusRefunded() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE + 1); + refund({caller: refunder, bridgeTx: tokenTx}); + vm.expectRevert(StatusIncorrect.selector); + fastBridge.canClaim(txId, relayerA); + vm.expectRevert(StatusIncorrect.selector); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + } + + // ══════════════════════════════════════════════════ DISPUTE ══════════════════════════════════════════════════════ + + function test_dispute_token() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + expectBridgeProofDisputed({txId: txId, guard: guard}); + dispute({caller: guard, txId: txId}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + } + + function test_dispute_token_justBeforeDeadline() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY); + expectBridgeProofDisputed({txId: txId, guard: guard}); + dispute({caller: guard, txId: txId}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + } + + function test_dispute_eth() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); + expectBridgeProofDisputed({txId: txId, guard: guard}); + dispute({caller: guard, txId: txId}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethParams.originAmount); + } + + function test_dispute_eth_justBeforeDeadline() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY); + expectBridgeProofDisputed({txId: txId, guard: guard}); + dispute({caller: guard, txId: txId}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethParams.originAmount); + } + + function test_dispute_revert_afterDeadline() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + vm.expectRevert(DisputePeriodPassed.selector); + dispute({caller: guard, txId: txId}); + } + + function test_dispute_revert_callerNotGuard(address caller) public { + vm.assume(caller != guard); + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + expectUnauthorized(caller, fastBridge.GUARD_ROLE()); + dispute({caller: caller, txId: txId}); + } + + function test_dispute_revert_statusNull() public { + bytes32 txId = getTxId(tokenTx); + vm.expectRevert(StatusIncorrect.selector); + dispute({caller: guard, txId: txId}); + } + + function test_dispute_revert_statusRequested() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + vm.expectRevert(StatusIncorrect.selector); + dispute({caller: guard, txId: txId}); + } + + function test_dispute_revert_statusClaimed() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + vm.expectRevert(StatusIncorrect.selector); + dispute({caller: guard, txId: txId}); + } + + function test_dispute_revert_statusRefunded() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE + 1); + refund({caller: refunder, bridgeTx: tokenTx}); + vm.expectRevert(StatusIncorrect.selector); + dispute({caller: guard, txId: txId}); + } + + // ══════════════════════════════════════════════════ REFUND ═══════════════════════════════════════════════════════ + + function test_refund_token() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE + 1); + expectBridgeDepositRefunded({bridgeParams: tokenParams, txId: txId}); + refund({caller: refunder, bridgeTx: tokenTx}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + /// @notice Deposit should be refunded to the BridgeParams.sender, regardless of the actual caller + function test_refund_token_diffSender() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userB, msgValue: 0, params: tokenParams}); + skip(DEADLINE + 1); + expectBridgeDepositRefunded({bridgeParams: tokenParams, txId: txId}); + refund({caller: refunder, bridgeTx: tokenTx}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + 2 * tokenParams.originAmount); + assertEq(srcToken.balanceOf(userB), LEFTOVER_BALANCE); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + function test_refund_token_longDelay() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE + 30 days); + expectBridgeDepositRefunded({bridgeParams: tokenParams, txId: txId}); + refund({caller: refunder, bridgeTx: tokenTx}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + function test_refund_token_permisionless(address caller) public { + vm.assume(caller != refunder); + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE + PERMISSIONLESS_REFUND_DELAY + 1); + expectBridgeDepositRefunded({bridgeParams: tokenParams, txId: txId}); + refund({caller: caller, bridgeTx: tokenTx}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + function test_refund_eth() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + skip(DEADLINE + 1); + expectBridgeDepositRefunded({bridgeParams: ethParams, txId: txId}); + refund({caller: refunder, bridgeTx: ethTx}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); + assertEq(address(userA).balance, LEFTOVER_BALANCE + ethParams.originAmount); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); + } + + /// @notice Deposit should be refunded to the BridgeParams.sender, regardless of the actual caller + function test_refund_eth_diffSender() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userB, msgValue: ethParams.originAmount, params: ethParams}); + skip(DEADLINE + 1); + expectBridgeDepositRefunded({bridgeParams: ethParams, txId: txId}); + refund({caller: refunder, bridgeTx: ethTx}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); + assertEq(address(userA).balance, LEFTOVER_BALANCE + 2 * ethParams.originAmount); + assertEq(address(userB).balance, LEFTOVER_BALANCE); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); + } + + function test_refund_eth_longDelay() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + skip(DEADLINE + 30 days); + expectBridgeDepositRefunded({bridgeParams: ethParams, txId: txId}); + refund({caller: refunder, bridgeTx: ethTx}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); + assertEq(address(userA).balance, LEFTOVER_BALANCE + ethParams.originAmount); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); + } + + function test_refund_eth_permisionless(address caller) public { + vm.assume(caller != refunder); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + skip(DEADLINE + PERMISSIONLESS_REFUND_DELAY + 1); + expectBridgeDepositRefunded({bridgeParams: ethParams, txId: txId}); + refund({caller: caller, bridgeTx: ethTx}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); + assertEq(address(userA).balance, LEFTOVER_BALANCE + ethParams.originAmount); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); + } + + function test_refund_revert_zeroDelay() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + vm.expectRevert(DeadlineNotExceeded.selector); + refund({caller: refunder, bridgeTx: ethTx}); + } + + function test_refund_revert_justBeforeDeadline() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE); + vm.expectRevert(DeadlineNotExceeded.selector); + refund({caller: refunder, bridgeTx: ethTx}); + } + + function test_refund_revert_justBeforeDeadline_permisionless(address caller) public { + vm.assume(caller != refunder); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE + PERMISSIONLESS_REFUND_DELAY); + vm.expectRevert(DeadlineNotExceeded.selector); + refund({caller: caller, bridgeTx: ethTx}); + } + + function test_refund_revert_statusNull() public { + vm.skip(true); // TODO: unskip when fixed + vm.expectRevert(StatusIncorrect.selector); + refund({caller: refunder, bridgeTx: ethTx}); + } + + function test_refund_revert_statusProven() public { + vm.skip(true); // TODO: unskip when fixed + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + vm.expectRevert(StatusIncorrect.selector); + refund({caller: refunder, bridgeTx: tokenTx}); + } + + function test_refund_revert_statusClaimed() public { + vm.skip(true); // TODO: unskip when fixed + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + vm.expectRevert(StatusIncorrect.selector); + refund({caller: refunder, bridgeTx: tokenTx}); + } + + function test_refund_revert_statusRefunded() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE + 1); + refund({caller: refunder, bridgeTx: tokenTx}); + vm.expectRevert(StatusIncorrect.selector); + refund({caller: refunder, bridgeTx: tokenTx}); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.t.sol b/packages/contracts-rfq/test/FastBridgeV2.t.sol new file mode 100644 index 0000000000..4fe357e3f2 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.t.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {IFastBridge} from "../contracts/interfaces/IFastBridge.sol"; +import {FastBridgeV2} from "../contracts/FastBridgeV2.sol"; + +import {MockERC20} from "./MockERC20.sol"; + +import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; +import {Test} from "forge-std/Test.sol"; +import {stdStorage, StdStorage} from "forge-std/Test.sol"; + +// solhint-disable no-empty-blocks +abstract contract FastBridgeV2Test is Test { + using stdStorage for StdStorage; + + uint32 public constant SRC_CHAIN_ID = 1337; + uint32 public constant DST_CHAIN_ID = 7331; + uint256 public constant DEADLINE = 1 days; + address public constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + + FastBridgeV2 public fastBridge; + MockERC20 public srcToken; + MockERC20 public dstToken; + + address public relayerA = makeAddr("Relayer A"); + address public relayerB = makeAddr("Relayer B"); + address public guard = makeAddr("Guard"); + address public userA = makeAddr("User A"); + address public userB = makeAddr("User B"); + address public governor = makeAddr("Governor"); + address public refunder = makeAddr("Refunder"); + + IFastBridge.BridgeTransaction public tokenTx; + IFastBridge.BridgeTransaction public ethTx; + IFastBridge.BridgeParams public tokenParams; + IFastBridge.BridgeParams public ethParams; + + function setUp() public virtual { + srcToken = new MockERC20("SrcToken", 6); + dstToken = new MockERC20("DstToken", 6); + createFixtures(); + fastBridge = deployFastBridge(); + configureFastBridge(); + mintTokens(); + } + + function deployFastBridge() public virtual returns (FastBridgeV2); + + function configureFastBridge() public virtual {} + + function mintTokens() public virtual {} + + function createFixtures() public virtual { + tokenParams = IFastBridge.BridgeParams({ + dstChainId: DST_CHAIN_ID, + sender: userA, + to: userB, + originToken: address(srcToken), + destToken: address(dstToken), + originAmount: 1e6, + destAmount: 0.99e6, + sendChainGas: false, + deadline: block.timestamp + DEADLINE + }); + ethParams = IFastBridge.BridgeParams({ + dstChainId: DST_CHAIN_ID, + sender: userA, + to: userB, + originToken: ETH_ADDRESS, + destToken: ETH_ADDRESS, + originAmount: 1 ether, + destAmount: 0.99 ether, + sendChainGas: false, + deadline: block.timestamp + DEADLINE + }); + + tokenTx = IFastBridge.BridgeTransaction({ + originChainId: SRC_CHAIN_ID, + destChainId: DST_CHAIN_ID, + originSender: userA, + destRecipient: userB, + originToken: address(srcToken), + destToken: address(dstToken), + originAmount: 1e6, + destAmount: 0.99e6, + // override this in tests with protocol fees + originFeeAmount: 0, + sendChainGas: false, + deadline: block.timestamp + DEADLINE, + nonce: 0 + }); + ethTx = IFastBridge.BridgeTransaction({ + originChainId: SRC_CHAIN_ID, + destChainId: DST_CHAIN_ID, + originSender: userA, + destRecipient: userB, + originToken: ETH_ADDRESS, + destToken: ETH_ADDRESS, + originAmount: 1 ether, + destAmount: 0.99 ether, + // override this in tests with protocol fees + originFeeAmount: 0, + sendChainGas: false, + deadline: block.timestamp + DEADLINE, + nonce: 1 + }); + } + + function getTxId(IFastBridge.BridgeTransaction memory bridgeTx) public pure returns (bytes32) { + return keccak256(abi.encode(bridgeTx)); + } + + function expectUnauthorized(address caller, bytes32 role) public { + vm.expectRevert(abi.encodeWithSelector(IAccessControl.AccessControlUnauthorizedAccount.selector, caller, role)); + } + + function cheatCollectedProtocolFees(address token, uint256 amount) public { + stdstore.target(address(fastBridge)).sig("protocolFees(address)").with_key(token).checked_write(amount); + } +} From dd67210c004b519bb38dbab9237eb065b16908cf Mon Sep 17 00:00:00 2001 From: parodime Date: Fri, 20 Sep 2024 20:47:36 +0000 Subject: [PATCH 05/90] Publish - FastBridge@0.4.0 --- packages/contracts-rfq/CHANGELOG.md | 11 +++++++++++ packages/contracts-rfq/package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/contracts-rfq/CHANGELOG.md b/packages/contracts-rfq/CHANGELOG.md index 4c55dc04d6..d0f588aa52 100644 --- a/packages/contracts-rfq/CHANGELOG.md +++ b/packages/contracts-rfq/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.4.0](https://github.com/synapsecns/sanguine/compare/FastBridge@0.3.0...FastBridge@0.4.0) (2024-09-20) + + +### Features + +* **contracts-rfq:** relay/prove/claim with different address [SLT-130] ([#3138](https://github.com/synapsecns/sanguine/issues/3138)) ([23f6c4c](https://github.com/synapsecns/sanguine/commit/23f6c4c652743c5ca7a184ad730ce19af3600a9c)) + + + + + # [0.3.0](https://github.com/synapsecns/sanguine/compare/FastBridge@0.2.14...FastBridge@0.3.0) (2024-09-10) diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index d778482a30..0cd9cb469a 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,7 +1,7 @@ { "name": "FastBridge", "license": "UNLICENSED", - "version": "0.3.0", + "version": "0.4.0", "description": "FastBridge contracts.", "private": true, "files": [ From 4a0f2e7a8842e9b5f53ff22e54317d20ae99b7a4 Mon Sep 17 00:00:00 2001 From: vro <168573323+golangisfun123@users.noreply.github.com> Date: Fri, 20 Sep 2024 19:33:22 -0500 Subject: [PATCH 06/90] fix(promexporter): make spans better (#3164) * move the errors * [goreleaser] * fix v to w --- contrib/promexporter/exporters/exporter.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/contrib/promexporter/exporters/exporter.go b/contrib/promexporter/exporters/exporter.go index d67c42fd8d..df8d4af7e4 100644 --- a/contrib/promexporter/exporters/exporter.go +++ b/contrib/promexporter/exporters/exporter.go @@ -142,17 +142,17 @@ func (e *exporter) collectMetrics(parentCtx context.Context) (err error) { for _, pending := range e.cfg.DFKPending { if err := e.stuckHeroCountStats(ctx, common.HexToAddress(pending.Owner), pending.ChainName); err != nil { errs = append(errs, fmt.Errorf("could not get stuck hero count: %w", err)) + span.AddEvent("could not get stuck hero count") } - span.AddEvent("could not get stuck hero count") } for _, gasCheck := range e.cfg.SubmitterChecks { for _, chainID := range gasCheck.ChainIDs { if err := e.submitterStats(common.HexToAddress(gasCheck.Address), chainID, gasCheck.Name); err != nil { - errs = append(errs, fmt.Errorf("could setup metric: %w", err)) + errs = append(errs, fmt.Errorf("could not get submitter stats: %w", err)) + span.AddEvent("could not get submitter stats") } } - span.AddEvent("could get submitter stats") } for chainID := range e.cfg.BridgeChecks { @@ -161,7 +161,8 @@ func (e *exporter) collectMetrics(parentCtx context.Context) (err error) { return retry.WithBackoff(ctx, func(ctx context.Context) error { err := e.vpriceStats(ctx, chainID, token) if err != nil && !errors.Is(err, errPoolNotExist) { - errs = append(errs, fmt.Errorf("stuck hero stats: %w", err)) + errs = append(errs, fmt.Errorf("could not get vprice %w", err)) + span.AddEvent("could not get vprice stats") } return nil @@ -172,7 +173,7 @@ func (e *exporter) collectMetrics(parentCtx context.Context) (err error) { if len(errs) > 0 { span.AddEvent("could not collect metrics") - return fmt.Errorf("could not collect metrics: %v", errs) + return fmt.Errorf("could not collect metrics: %w", combineErrors(errs)) } return nil From 1eb52a0c78386365efbc66a6a17e24e9b882bd25 Mon Sep 17 00:00:00 2001 From: Moses <103143573+Defi-Moses@users.noreply.github.com> Date: Sat, 21 Sep 2024 11:49:40 +0100 Subject: [PATCH 07/90] changing native token address standard [SLT-210] (#3157) * changing native token address standard * fixing tests * normalizeNativeTokenAddress middleware, additional tests --------- Co-authored-by: abtestingalpha --- packages/rest-api/src/constants/bridgeable.ts | 31 +++++++------ .../middleware/normalizeNativeTokenAddress.ts | 18 ++++++++ packages/rest-api/src/routes/bridgeRoute.ts | 2 + .../rest-api/src/routes/bridgeTxInfoRoute.ts | 2 + .../src/routes/destinationTokensRoute.ts | 2 + packages/rest-api/src/routes/swapRoute.ts | 2 + .../rest-api/src/routes/swapTxInfoRoute.ts | 2 + .../rest-api/src/tests/bridgeRoute.test.ts | 41 +++++++++++++++-- .../src/tests/bridgeTxInfoRoute.test.ts | 13 +++--- .../src/tests/destinationTokensRoute.test.ts | 28 ++++++++--- packages/rest-api/src/tests/swapRoute.test.ts | 46 +++++++++++++++---- .../src/tests/swapTxInfoRoute.test.ts | 21 +++++---- .../rest-api/src/tests/tokenListRoute.test.ts | 2 +- .../rest-api/src/utils/tokenAddressToToken.ts | 5 +- 14 files changed, 162 insertions(+), 53 deletions(-) create mode 100644 packages/rest-api/src/middleware/normalizeNativeTokenAddress.ts diff --git a/packages/rest-api/src/constants/bridgeable.ts b/packages/rest-api/src/constants/bridgeable.ts index e0bbd61d80..1a9b25a99c 100644 --- a/packages/rest-api/src/constants/bridgeable.ts +++ b/packages/rest-api/src/constants/bridgeable.ts @@ -1,6 +1,7 @@ import { BridgeableToken } from '../types' import { CHAINS } from './chains' -import { ZeroAddress } from '.' + +const NativeTokenAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' export const GOHM: BridgeableToken = { addresses: { @@ -796,16 +797,16 @@ export const NETH: BridgeableToken = { export const ETH: BridgeableToken = { addresses: { - [CHAINS.ETHEREUM.id]: ZeroAddress, - [CHAINS.OPTIMISM.id]: ZeroAddress, - [CHAINS.BOBA.id]: ZeroAddress, + [CHAINS.ETHEREUM.id]: NativeTokenAddress, + [CHAINS.OPTIMISM.id]: NativeTokenAddress, + [CHAINS.BOBA.id]: NativeTokenAddress, [CHAINS.CANTO.id]: '0x5FD55A1B9FC24967C4dB09C513C3BA0DFa7FF687', - [CHAINS.BASE.id]: ZeroAddress, - [CHAINS.ARBITRUM.id]: ZeroAddress, + [CHAINS.BASE.id]: NativeTokenAddress, + [CHAINS.ARBITRUM.id]: NativeTokenAddress, [CHAINS.DFK.id]: '0xfBDF0E31808d0aa7b9509AA6aBC9754E48C58852', - [CHAINS.BLAST.id]: ZeroAddress, - [CHAINS.SCROLL.id]: ZeroAddress, - [CHAINS.LINEA.id]: ZeroAddress, + [CHAINS.BLAST.id]: NativeTokenAddress, + [CHAINS.SCROLL.id]: NativeTokenAddress, + [CHAINS.LINEA.id]: NativeTokenAddress, }, decimals: { [CHAINS.ETHEREUM.id]: 18, @@ -832,7 +833,7 @@ export const ETH: BridgeableToken = { export const MOVR: BridgeableToken = { addresses: { [CHAINS.MOONBEAM.id]: '0x1d4C2a246311bB9f827F4C768e277FF5787B7D7E', - [CHAINS.MOONRIVER.id]: ZeroAddress, + [CHAINS.MOONRIVER.id]: NativeTokenAddress, }, decimals: { [CHAINS.MOONBEAM.id]: 18, @@ -852,7 +853,7 @@ export const AVAX: BridgeableToken = { addresses: { [CHAINS.MOONBEAM.id]: '0xA1f8890E39b4d8E33efe296D698fe42Fb5e59cC3', [CHAINS.KLAYTN.id]: '0xCd8fE44A29Db9159dB36f96570d7A4d91986f528', - [CHAINS.AVALANCHE.id]: ZeroAddress, + [CHAINS.AVALANCHE.id]: NativeTokenAddress, [CHAINS.DFK.id]: '0xB57B60DeBDB0b8172bb6316a9164bd3C695F133a', [CHAINS.HARMONY.id]: '0xb12c13e66AdE1F72f71834f2FC5082Db8C091358', }, @@ -909,7 +910,7 @@ export const WAVAX: BridgeableToken = { export const JEWEL: BridgeableToken = { addresses: { - [CHAINS.DFK.id]: ZeroAddress, + [CHAINS.DFK.id]: NativeTokenAddress, [CHAINS.HARMONY.id]: '0x72cb10c6bfa5624dd07ef608027e366bd690048f', [CHAINS.KLAYTN.id]: '0x30C103f8f5A3A732DFe2dCE1Cc9446f545527b43', [CHAINS.AVALANCHE.id]: '0x997Ddaa07d716995DE90577C123Db411584E5E46', @@ -1158,7 +1159,7 @@ export const DAIe: BridgeableToken = { export const KLAY: BridgeableToken = { addresses: { - [CHAINS.KLAYTN.id]: ZeroAddress, + [CHAINS.KLAYTN.id]: NativeTokenAddress, [CHAINS.DFK.id]: '0x97855Ba65aa7ed2F65Ed832a776537268158B78a', }, decimals: { @@ -1194,7 +1195,7 @@ export const WKLAY: BridgeableToken = { export const MATIC: BridgeableToken = { addresses: { - [CHAINS.POLYGON.id]: ZeroAddress, + [CHAINS.POLYGON.id]: NativeTokenAddress, [CHAINS.DFK.id]: '0xD17a41Cd199edF1093A9Be4404EaDe52Ec19698e', }, decimals: { @@ -1230,7 +1231,7 @@ export const WMATIC: BridgeableToken = { export const FTM: BridgeableToken = { addresses: { - [CHAINS.FANTOM.id]: ZeroAddress, + [CHAINS.FANTOM.id]: NativeTokenAddress, [CHAINS.DFK.id]: '0x2Df041186C844F8a2e2b63F16145Bc6Ff7d23E25', }, decimals: { diff --git a/packages/rest-api/src/middleware/normalizeNativeTokenAddress.ts b/packages/rest-api/src/middleware/normalizeNativeTokenAddress.ts new file mode 100644 index 0000000000..546c08136e --- /dev/null +++ b/packages/rest-api/src/middleware/normalizeNativeTokenAddress.ts @@ -0,0 +1,18 @@ +import { Request, Response, NextFunction } from 'express' +import { isAddress, getAddress } from 'ethers/lib/utils' + +import { NativeGasAddress, ZeroAddress } from '../constants' + +export const normalizeNativeTokenAddress = (addressFields: string[]) => { + return (req: Request, _res: Response, next: NextFunction) => { + for (const field of addressFields) { + const address = req.query[field] + if (typeof address === 'string' && isAddress(address)) { + const checksumAddress = getAddress(address) + req.query[field] = + checksumAddress === ZeroAddress ? NativeGasAddress : checksumAddress + } + } + next() + } +} diff --git a/packages/rest-api/src/routes/bridgeRoute.ts b/packages/rest-api/src/routes/bridgeRoute.ts index 3ee858fcb3..01fccb708b 100644 --- a/packages/rest-api/src/routes/bridgeRoute.ts +++ b/packages/rest-api/src/routes/bridgeRoute.ts @@ -7,6 +7,7 @@ import { showFirstValidationError } from '../middleware/showFirstValidationError import { bridgeController } from '../controllers/bridgeController' import { isTokenSupportedOnChain } from '../utils/isTokenSupportedOnChain' import { checksumAddresses } from '../middleware/checksumAddresses' +import { normalizeNativeTokenAddress } from '../middleware/normalizeNativeTokenAddress' const router = express.Router() @@ -187,6 +188,7 @@ const router = express.Router() */ router.get( '/', + normalizeNativeTokenAddress(['fromToken', 'toToken']), checksumAddresses(['fromToken', 'toToken']), [ check('fromChain') diff --git a/packages/rest-api/src/routes/bridgeTxInfoRoute.ts b/packages/rest-api/src/routes/bridgeTxInfoRoute.ts index 4336711511..09f249b99b 100644 --- a/packages/rest-api/src/routes/bridgeTxInfoRoute.ts +++ b/packages/rest-api/src/routes/bridgeTxInfoRoute.ts @@ -8,6 +8,7 @@ import { bridgeTxInfoController } from '../controllers/bridgeTxInfoController' import { isTokenAddress } from '../utils/isTokenAddress' import { isTokenSupportedOnChain } from '../utils/isTokenSupportedOnChain' import { checksumAddresses } from '../middleware/checksumAddresses' +import { normalizeNativeTokenAddress } from '../middleware/normalizeNativeTokenAddress' const router = express.Router() @@ -123,6 +124,7 @@ const router = express.Router() */ router.get( '/', + normalizeNativeTokenAddress(['fromToken', 'toToken']), checksumAddresses(['fromToken', 'toToken']), [ check('fromChain') diff --git a/packages/rest-api/src/routes/destinationTokensRoute.ts b/packages/rest-api/src/routes/destinationTokensRoute.ts index f06ac9e4f6..a6e4c590d4 100644 --- a/packages/rest-api/src/routes/destinationTokensRoute.ts +++ b/packages/rest-api/src/routes/destinationTokensRoute.ts @@ -8,6 +8,7 @@ import { destinationTokensController } from '../controllers/destinationTokensCon import { isTokenAddress } from '../utils/isTokenAddress' import { isTokenSupportedOnChain } from '../utils/isTokenSupportedOnChain' import { checksumAddresses } from '../middleware/checksumAddresses' +import { normalizeNativeTokenAddress } from '../middleware/normalizeNativeTokenAddress' const router = express.Router() @@ -98,6 +99,7 @@ const router = express.Router() router.get( '/', + normalizeNativeTokenAddress(['fromToken']), checksumAddresses(['fromToken']), [ check('fromChain') diff --git a/packages/rest-api/src/routes/swapRoute.ts b/packages/rest-api/src/routes/swapRoute.ts index ee65c015b0..8c050f3b2b 100644 --- a/packages/rest-api/src/routes/swapRoute.ts +++ b/packages/rest-api/src/routes/swapRoute.ts @@ -7,6 +7,7 @@ import { CHAINS_ARRAY } from '../constants/chains' import { isTokenAddress } from '../utils/isTokenAddress' import { isTokenSupportedOnChain } from '../utils/isTokenSupportedOnChain' import { checksumAddresses } from '../middleware/checksumAddresses' +import { normalizeNativeTokenAddress } from '../middleware/normalizeNativeTokenAddress' const router = express.Router() @@ -133,6 +134,7 @@ const router = express.Router() */ router.get( '/', + normalizeNativeTokenAddress(['fromToken', 'toToken']), checksumAddresses(['fromToken', 'toToken']), [ check('chain') diff --git a/packages/rest-api/src/routes/swapTxInfoRoute.ts b/packages/rest-api/src/routes/swapTxInfoRoute.ts index 2ffb03388f..093dc5686f 100644 --- a/packages/rest-api/src/routes/swapTxInfoRoute.ts +++ b/packages/rest-api/src/routes/swapTxInfoRoute.ts @@ -8,6 +8,7 @@ import { swapTxInfoController } from '../controllers/swapTxInfoController' import { isTokenAddress } from '../utils/isTokenAddress' import { isTokenSupportedOnChain } from '../utils/isTokenSupportedOnChain' import { checksumAddresses } from '../middleware/checksumAddresses' +import { normalizeNativeTokenAddress } from '../middleware/normalizeNativeTokenAddress' const router = express.Router() @@ -115,6 +116,7 @@ const router = express.Router() */ router.get( '/', + normalizeNativeTokenAddress(['fromToken', 'toToken']), checksumAddresses(['fromToken', 'toToken']), [ check('chain') diff --git a/packages/rest-api/src/tests/bridgeRoute.test.ts b/packages/rest-api/src/tests/bridgeRoute.test.ts index dcef8451ec..756089f50b 100644 --- a/packages/rest-api/src/tests/bridgeRoute.test.ts +++ b/packages/rest-api/src/tests/bridgeRoute.test.ts @@ -2,6 +2,8 @@ import request from 'supertest' import express from 'express' import bridgeRoute from '../routes/bridgeRoute' +import { NativeGasAddress, ZeroAddress } from '../constants' +import { USDC } from '../constants/bridgeable' const app = express() app.use('/bridge', bridgeRoute) @@ -11,8 +13,8 @@ describe('Bridge Route with Real Synapse Service', () => { const response = await request(app).get('/bridge').query({ fromChain: '1', toChain: '10', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC on Ethereum - toToken: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', // USDC on Optimism + fromToken: USDC.addresses[1], + toToken: USDC.addresses[10], amount: '1000', }) expect(response.status).toBe(200) @@ -22,6 +24,37 @@ describe('Bridge Route with Real Synapse Service', () => { expect(response.body[0]).toHaveProperty('bridgeFeeFormatted') }, 15000) + it('should return bridge quotes for ZeroAddress', async () => { + const response = await request(app).get('/bridge').query({ + fromChain: '1', + toChain: '10', + fromToken: ZeroAddress, + toToken: ZeroAddress, + amount: '10', + }) + expect(response.status).toBe(200) + expect(Array.isArray(response.body)).toBe(true) + expect(response.body.length).toBeGreaterThan(0) + expect(response.body[0]).toHaveProperty('maxAmountOutStr') + expect(response.body[0]).toHaveProperty('bridgeFeeFormatted') + }, 15000) + + it('should return bridge quotes for NativeGasAddress', async () => { + const response = await request(app).get('/bridge').query({ + fromChain: '1', + toChain: '10', + fromToken: NativeGasAddress, + toToken: NativeGasAddress, + amount: '10', + }) + + expect(response.status).toBe(200) + expect(Array.isArray(response.body)).toBe(true) + expect(response.body.length).toBeGreaterThan(0) + expect(response.body[0]).toHaveProperty('maxAmountOutStr') + expect(response.body[0]).toHaveProperty('bridgeFeeFormatted') + }, 15000) + it('should return 400 for unsupported fromChain, with error message', async () => { const response = await request(app).get('/bridge').query({ fromChain: '999', @@ -83,8 +116,8 @@ describe('Bridge Route with Real Synapse Service', () => { const response = await request(app).get('/bridge').query({ fromChain: '1', toChain: '10', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - toToken: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', + fromToken: USDC.addresses[1], + toToken: USDC.addresses[10], }) expect(response.status).toBe(400) expect(response.body.error).toHaveProperty('field', 'amount') diff --git a/packages/rest-api/src/tests/bridgeTxInfoRoute.test.ts b/packages/rest-api/src/tests/bridgeTxInfoRoute.test.ts index 35ff81f19a..822491f887 100644 --- a/packages/rest-api/src/tests/bridgeTxInfoRoute.test.ts +++ b/packages/rest-api/src/tests/bridgeTxInfoRoute.test.ts @@ -2,6 +2,7 @@ import request from 'supertest' import express from 'express' import bridgeTxInfoRoute from '../routes/bridgeTxInfoRoute' +import { USDC } from '../constants/bridgeable' const app = express() app.use('/bridgeTxInfo', bridgeTxInfoRoute) @@ -11,8 +12,8 @@ describe('Bridge TX Info Route', () => { const response = await request(app).get('/bridgeTxInfo').query({ fromChain: '1', toChain: '137', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC on Ethereum - toToken: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', // USDC on Polygon + fromToken: USDC.addresses[1], + toToken: USDC.addresses[137], amount: '1000', destAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', }) @@ -79,8 +80,8 @@ describe('Bridge TX Info Route', () => { const response = await request(app).get('/bridgeTxInfo').query({ fromChain: '1', toChain: '137', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - toToken: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', + fromToken: USDC.addresses[1], + toToken: USDC.addresses[137], destAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', }) expect(response.status).toBe(400) @@ -91,8 +92,8 @@ describe('Bridge TX Info Route', () => { const response = await request(app).get('/bridgeTxInfo').query({ fromChain: '1', toChain: '137', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - toToken: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', + fromToken: USDC.addresses[1], + toToken: USDC.addresses[137], amount: '1000', destAddress: 'invalid_address', }) diff --git a/packages/rest-api/src/tests/destinationTokensRoute.test.ts b/packages/rest-api/src/tests/destinationTokensRoute.test.ts index 4fe40a7a62..a083741ffa 100644 --- a/packages/rest-api/src/tests/destinationTokensRoute.test.ts +++ b/packages/rest-api/src/tests/destinationTokensRoute.test.ts @@ -2,6 +2,8 @@ import request from 'supertest' import express from 'express' import destinationTokensRoute from '../routes/destinationTokensRoute' +import { NativeGasAddress, ZeroAddress } from '../constants' +import { USDC, USDT } from '../constants/bridgeable' const app = express() app.use('/destinationTokens', destinationTokensRoute) @@ -10,7 +12,7 @@ describe('destinatonTokens Route', () => { it('should return destination tokens for valid input', async () => { const response = await request(app).get('/destinationTokens').query({ fromChain: '1', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + fromToken: USDC.addresses[1], }) expect(response.status).toBe(200) @@ -24,7 +26,21 @@ describe('destinatonTokens Route', () => { it('should return destination tokens for valid gas Tokens', async () => { const response = await request(app).get('/destinationTokens').query({ fromChain: '1', - fromToken: '0x0000000000000000000000000000000000000000', + fromToken: NativeGasAddress, + }) + + expect(response.status).toBe(200) + expect(Array.isArray(response.body)).toBe(true) + expect(response.body.length).toBeGreaterThan(0) + expect(response.body[0]).toHaveProperty('symbol') + expect(response.body[0]).toHaveProperty('address') + expect(response.body[0]).toHaveProperty('chainId') + }) + + it('should return destination tokens for valid gas Tokens, ZeroAddress', async () => { + const response = await request(app).get('/destinationTokens').query({ + fromChain: '1', + fromToken: ZeroAddress, }) expect(response.status).toBe(200) @@ -40,7 +56,7 @@ describe('destinatonTokens Route', () => { const response = await request(app).get('/destinationTokens').query({ fromChain: '534352', - fromToken: '0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4', + fromToken: USDC.addresses[534352], }) expect(response.status).toBe(200) @@ -54,7 +70,7 @@ describe('destinatonTokens Route', () => { it('should return destination tokens for non-checksummed address', async () => { const response = await request(app).get('/destinationTokens').query({ fromChain: '43114', - fromToken: '0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7', + fromToken: USDT.addresses[43114].toLowerCase(), }) expect(response.status).toBe(200) @@ -107,7 +123,7 @@ describe('destinatonTokens Route', () => { it('should return 400 for token not supported on specified chain', async () => { const response = await request(app).get('/destinationTokens').query({ fromChain: '10', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + fromToken: USDC.addresses[1], }) expect(response.status).toBe(400) @@ -119,7 +135,7 @@ describe('destinatonTokens Route', () => { it('should return 400 for missing fromChain', async () => { const response = await request(app).get('/destinationTokens').query({ - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + fromToken: USDC.addresses[1], }) expect(response.status).toBe(400) diff --git a/packages/rest-api/src/tests/swapRoute.test.ts b/packages/rest-api/src/tests/swapRoute.test.ts index 6c6d7ac43b..775372bc31 100644 --- a/packages/rest-api/src/tests/swapRoute.test.ts +++ b/packages/rest-api/src/tests/swapRoute.test.ts @@ -2,6 +2,8 @@ import request from 'supertest' import express from 'express' import swapRoute from '../routes/swapRoute' +import { NativeGasAddress, ZeroAddress } from '../constants' +import { DAI, NETH, USDC } from '../constants/bridgeable' const app = express() app.use('/swap', swapRoute) @@ -10,8 +12,8 @@ describe('Swap Route with Real Synapse Service', () => { it('should return a real swap quote for valid input, 1000 USDC', async () => { const response = await request(app).get('/swap').query({ chain: '1', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC on Ethereum - toToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI on Ethereum + fromToken: USDC.addresses[1], + toToken: DAI.addresses[1], amount: '1000', }) @@ -21,11 +23,39 @@ describe('Swap Route with Real Synapse Service', () => { expect(response.body).toHaveProperty('query') }, 10_000) + it('should return a real swap quote for valid input, Eth ZeroAddress', async () => { + const response = await request(app).get('/swap').query({ + chain: '10', + fromToken: ZeroAddress, + toToken: NETH.addresses[10], + amount: '1', + }) + + expect(response.status).toBe(200) + expect(response.body).toHaveProperty('maxAmountOut') + expect(response.body).toHaveProperty('routerAddress') + expect(response.body).toHaveProperty('query') + }, 10_000) + + it('should return a real swap quote for valid input, Eth NativeGasAddress', async () => { + const response = await request(app).get('/swap').query({ + chain: '10', + fromToken: NativeGasAddress, + toToken: NETH.addresses[10], + amount: '1', + }) + + expect(response.status).toBe(200) + expect(response.body).toHaveProperty('maxAmountOut') + expect(response.body).toHaveProperty('routerAddress') + expect(response.body).toHaveProperty('query') + }, 10_000) + it('should return 400 for unsupported chain, with error message', async () => { const response = await request(app).get('/swap').query({ chain: '111', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - toToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + fromToken: USDC.addresses[1], + toToken: DAI.addresses[1], amount: '1000', }) @@ -36,7 +66,7 @@ describe('Swap Route with Real Synapse Service', () => { it('should return 400 for invalid toToken address, with error message', async () => { const response = await request(app).get('/swap').query({ chain: '1', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + fromToken: USDC.addresses[1], toToken: 'invalid_address', amount: '1000', }) @@ -51,7 +81,7 @@ describe('Swap Route with Real Synapse Service', () => { it('should return 400 for token not supported on specified chain', async () => { const response = await request(app).get('/swap').query({ chain: '1', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + fromToken: USDC.addresses[1], toToken: '0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F', // SNX on Ethereum (Not supported) amount: '1000', }) @@ -66,8 +96,8 @@ describe('Swap Route with Real Synapse Service', () => { it('should return 400 for missing amount, with error message', async () => { const response = await request(app).get('/swap').query({ chain: '1', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - toToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + fromToken: USDC.addresses[1], + toToken: DAI.addresses[1], }) expect(response.status).toBe(400) diff --git a/packages/rest-api/src/tests/swapTxInfoRoute.test.ts b/packages/rest-api/src/tests/swapTxInfoRoute.test.ts index 3faf9ca8ea..99ea0e0f42 100644 --- a/packages/rest-api/src/tests/swapTxInfoRoute.test.ts +++ b/packages/rest-api/src/tests/swapTxInfoRoute.test.ts @@ -2,6 +2,7 @@ import request from 'supertest' import express from 'express' import swapTxInfoRoute from '../routes/swapTxInfoRoute' +import { DAI, USDC } from '../constants/bridgeable' const app = express() app.use('/swapTxInfo', swapTxInfoRoute) @@ -10,8 +11,8 @@ describe('Swap TX Info Route with Real Synapse Service', () => { it('should return transaction info for valid input, 1000 USDC to DAI', async () => { const response = await request(app).get('/swapTxInfo').query({ chain: '1', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC on Ethereum - toToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI on Ethereum + fromToken: USDC.addresses[1], + toToken: DAI.addresses[1], amount: '1000', address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', }) @@ -24,8 +25,8 @@ describe('Swap TX Info Route with Real Synapse Service', () => { it('should return 400 for invalid address, with error message', async () => { const response = await request(app).get('/swapTxInfo').query({ chain: '1', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - toToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + fromToken: USDC.addresses[1], + toToken: DAI.addresses[1], amount: '1000', address: 'invalid_address', }) @@ -39,8 +40,8 @@ describe('Swap TX Info Route with Real Synapse Service', () => { it('should return 400 for unsupported chain, with error message', async () => { const response = await request(app).get('/swapTxInfo').query({ chain: '111', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - toToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + fromToken: USDC.addresses[1], + toToken: DAI.addresses[1], amount: '1000', address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', }) @@ -51,7 +52,7 @@ describe('Swap TX Info Route with Real Synapse Service', () => { it('should return 400 for invalid toToken address, with error message', async () => { const response = await request(app).get('/swapTxInfo').query({ chain: '1', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + fromToken: USDC.addresses[1], toToken: 'invalid_address', amount: '1000', address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', @@ -66,7 +67,7 @@ describe('Swap TX Info Route with Real Synapse Service', () => { it('should return 400 for token not supported on specified chain', async () => { const response = await request(app).get('/swapTxInfo').query({ chain: '1', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + fromToken: USDC.addresses[1], toToken: '0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F', // SNX on Ethereum (Not supported) amount: '1000', address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', @@ -81,8 +82,8 @@ describe('Swap TX Info Route with Real Synapse Service', () => { it('should return 400 for missing amount, with error message', async () => { const response = await request(app).get('/swapTxInfo').query({ chain: '1', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - toToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + fromToken: USDC.addresses[1], + toToken: DAI.addresses[1], address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', }) expect(response.status).toBe(400) diff --git a/packages/rest-api/src/tests/tokenListRoute.test.ts b/packages/rest-api/src/tests/tokenListRoute.test.ts index 16d2fb82df..127ea9fb12 100644 --- a/packages/rest-api/src/tests/tokenListRoute.test.ts +++ b/packages/rest-api/src/tests/tokenListRoute.test.ts @@ -16,7 +16,7 @@ describe('Index Route', () => { expect(keys.length).toBe(62) expect(response.body['ETH']['addresses']['1']).toBe( - '0x0000000000000000000000000000000000000000' + '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' ) expect(response.body['SYN']['addresses']['1']).toBe( '0x0f2d719407fdbeff09d87557abb7232601fd9f29' diff --git a/packages/rest-api/src/utils/tokenAddressToToken.ts b/packages/rest-api/src/utils/tokenAddressToToken.ts index 21c073e7d5..6bad892582 100644 --- a/packages/rest-api/src/utils/tokenAddressToToken.ts +++ b/packages/rest-api/src/utils/tokenAddressToToken.ts @@ -1,4 +1,3 @@ -import { NativeGasAddress, ZeroAddress } from '../constants' import { BRIDGE_MAP } from '../constants/bridgeMap' export const tokenAddressToToken = (chain: string, tokenAddress: string) => { @@ -7,12 +6,12 @@ export const tokenAddressToToken = (chain: string, tokenAddress: string) => { return null } - const address = tokenAddress === ZeroAddress ? NativeGasAddress : tokenAddress + const tokenInfo = chainData[tokenAddress] - const tokenInfo = chainData[address] if (!tokenInfo) { return null } + return { address: tokenAddress, symbol: tokenInfo.symbol, From 3b507bdca622dd37281fd39d2dae2aedd7043675 Mon Sep 17 00:00:00 2001 From: Defi-Moses Date: Sat, 21 Sep 2024 10:53:50 +0000 Subject: [PATCH 08/90] Publish - @synapsecns/rest-api@1.1.4 --- packages/rest-api/CHANGELOG.md | 8 ++++++++ packages/rest-api/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/rest-api/CHANGELOG.md b/packages/rest-api/CHANGELOG.md index f65c063354..b4e0738437 100644 --- a/packages/rest-api/CHANGELOG.md +++ b/packages/rest-api/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.1.4](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.1.3...@synapsecns/rest-api@1.1.4) (2024-09-21) + +**Note:** Version bump only for package @synapsecns/rest-api + + + + + ## [1.1.3](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.1.2...@synapsecns/rest-api@1.1.3) (2024-09-20) **Note:** Version bump only for package @synapsecns/rest-api diff --git a/packages/rest-api/package.json b/packages/rest-api/package.json index 10db70e669..a6f4d23916 100644 --- a/packages/rest-api/package.json +++ b/packages/rest-api/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/rest-api", - "version": "1.1.3", + "version": "1.1.4", "private": "true", "engines": { "node": ">=18.17.0" From f4101f2aa2bed2e5595b6c5660aa479e536014cd Mon Sep 17 00:00:00 2001 From: Moses <103143573+Defi-Moses@users.noreply.github.com> Date: Sun, 22 Sep 2024 19:59:45 +0100 Subject: [PATCH 09/90] Refactoring rfq-indexer API and adding swagger docs [SLT-228] (#3167) * refactoring and adding swagger * remove testing scripts * fix typos and consistency with 404 errors --- packages/rfq-indexer/api/package.json | 26 ++- packages/rfq-indexer/api/src/.babelrc | 3 + packages/rfq-indexer/api/src/app.ts | 32 ++++ .../conflictingProofsController.ts | 50 ++++++ .../controllers/invalidRelaysController.ts | 52 ++++++ .../pendingTransactionsController.ts | 129 +++++++++++++++ .../refundedAndRelayedController.ts | 43 +++++ .../controllers/transactionIdController.ts | 58 +++++++ packages/rfq-indexer/api/src/index.ts | 100 ------------ packages/rfq-indexer/api/src/jest.config.js | 10 ++ .../middleware/showFirstValidationError.ts | 24 +++ .../api/src/queries/claimsQueries.ts | 17 ++ .../api/src/queries/depositsQueries.ts | 26 +++ packages/rfq-indexer/api/src/queries/index.ts | 5 + .../api/src/queries/proofsQueries.ts | 15 ++ .../api/src/queries/refundsQueries.ts | 15 ++ .../api/src/queries/relaysQueries.ts | 16 ++ .../api/src/routes/conflictingProofsRoute.ts | 65 ++++++++ packages/rfq-indexer/api/src/routes/index.ts | 17 ++ .../api/src/routes/invalidRelaysRoute.ts | 65 ++++++++ .../src/routes/pendingTransactionsRoute.ts | 149 ++++++++++++++++++ .../api/src/routes/refundedAndRelayedRoute.ts | 65 ++++++++ .../api/src/routes/transactionIdRoute.ts | 70 ++++++++ packages/rfq-indexer/api/src/swagger.ts | 28 ++++ .../api/src/utils/isTransaction.ts | 0 .../rfq-indexer/api/src/utils/nestResults.ts | 58 +++++++ yarn.lock | 36 ++++- 27 files changed, 1065 insertions(+), 109 deletions(-) create mode 100644 packages/rfq-indexer/api/src/.babelrc create mode 100644 packages/rfq-indexer/api/src/app.ts create mode 100644 packages/rfq-indexer/api/src/controllers/conflictingProofsController.ts create mode 100644 packages/rfq-indexer/api/src/controllers/invalidRelaysController.ts create mode 100644 packages/rfq-indexer/api/src/controllers/pendingTransactionsController.ts create mode 100644 packages/rfq-indexer/api/src/controllers/refundedAndRelayedController.ts create mode 100644 packages/rfq-indexer/api/src/controllers/transactionIdController.ts delete mode 100644 packages/rfq-indexer/api/src/index.ts create mode 100644 packages/rfq-indexer/api/src/jest.config.js create mode 100644 packages/rfq-indexer/api/src/middleware/showFirstValidationError.ts create mode 100644 packages/rfq-indexer/api/src/queries/claimsQueries.ts create mode 100644 packages/rfq-indexer/api/src/queries/depositsQueries.ts create mode 100644 packages/rfq-indexer/api/src/queries/index.ts create mode 100644 packages/rfq-indexer/api/src/queries/proofsQueries.ts create mode 100644 packages/rfq-indexer/api/src/queries/refundsQueries.ts create mode 100644 packages/rfq-indexer/api/src/queries/relaysQueries.ts create mode 100644 packages/rfq-indexer/api/src/routes/conflictingProofsRoute.ts create mode 100644 packages/rfq-indexer/api/src/routes/index.ts create mode 100644 packages/rfq-indexer/api/src/routes/invalidRelaysRoute.ts create mode 100644 packages/rfq-indexer/api/src/routes/pendingTransactionsRoute.ts create mode 100644 packages/rfq-indexer/api/src/routes/refundedAndRelayedRoute.ts create mode 100644 packages/rfq-indexer/api/src/routes/transactionIdRoute.ts create mode 100644 packages/rfq-indexer/api/src/swagger.ts create mode 100644 packages/rfq-indexer/api/src/utils/isTransaction.ts create mode 100644 packages/rfq-indexer/api/src/utils/nestResults.ts diff --git a/packages/rfq-indexer/api/package.json b/packages/rfq-indexer/api/package.json index dda045192c..534967fbf3 100644 --- a/packages/rfq-indexer/api/package.json +++ b/packages/rfq-indexer/api/package.json @@ -6,17 +6,18 @@ "main": "index.js", "scripts": { "check-env": "dotenv -e .env.local -- printenv | grep DATABASE_URL", - "dev:local": "dotenv -e .env.local -- tsx watch src/index.ts", - "dev:prod": "dotenv -e .env.production -- tsx watch src/index.ts", - "start": "tsx src/index.ts", - "start:local": "dotenv -e .env -- tsx src/index.ts", - "dev": "dotenv -e .env -- tsx watch src/index.ts", + "dev:local": "dotenv -e .env.local -- tsx watch src/app.ts", + "dev:prod": "dotenv -e .env.production -- tsx watch src/app.ts", + "start": "tsx src/app.ts", + "start:local": "dotenv -e .env -- tsx src/app.ts", + "dev": "dotenv -e .env -- tsx watch src/app.ts", "lint:check": " ", "ci:lint": " ", "build:go": " ", "build": " ", "build:slither": " ", - "test:coverage": "echo 'No tests defined.'" + "test": "", + "test:coverage": "echo no tests defined" }, "keywords": [], "author": "", @@ -29,10 +30,12 @@ "@types/node": "^22.5.4", "dotenv-cli": "^7.4.2", "express": "^4.21.0", + "express-validator": "^7.2.0", "graphql": "^16.9.0", "graphql-yoga": "^5.7.0", "kysely": "^0.27.4", "pg": "^8.12.0", + "supertest": "^7.0.0", "ts-node": "^10.9.2", "tsx": "^4.19.1", "typescript": "^5.6.2", @@ -42,8 +45,17 @@ "node": ">=18.17" }, "devDependencies": { + "@babel/core": "^7.25.2", + "@babel/preset-env": "^7.25.4", + "@babel/preset-typescript": "^7.24.7", "@types/pg": "^8.11.9", - "dotenv": "^16.4.5" + "@types/supertest": "^6.0.2", + "@types/swagger-jsdoc": "6.0.4", + "@types/swagger-ui-express": "4.1.6", + "dotenv": "^16.4.5", + "express-validator": "^7.2.0", + "swagger-jsdoc": "^6.2.8", + "swagger-ui-express": "^5.0.1" }, "repository": { "type": "git", diff --git a/packages/rfq-indexer/api/src/.babelrc b/packages/rfq-indexer/api/src/.babelrc new file mode 100644 index 0000000000..3313ff9ef0 --- /dev/null +++ b/packages/rfq-indexer/api/src/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@babel/preset-env", "@babel/preset-typescript"] +} diff --git a/packages/rfq-indexer/api/src/app.ts b/packages/rfq-indexer/api/src/app.ts new file mode 100644 index 0000000000..4209656836 --- /dev/null +++ b/packages/rfq-indexer/api/src/app.ts @@ -0,0 +1,32 @@ +import express from 'express' +import swaggerUi from 'swagger-ui-express' +import { createYoga } from 'graphql-yoga' + +import { specs } from './swagger' +import routes from './routes' +import { schema } from './graphql/schema' +import { overrideJsonBigIntSerialization } from './utils/overrideJsonBigIntSerialization' + +const app = express() +const port = process.env.PORT || 3001 + +overrideJsonBigIntSerialization() + +app.use(express.json()) + +// Swagger UI setup +app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs)) + +// REST API routes +app.use('/api', routes) + +// GraphQL setup +const yoga = createYoga({ schema }) +app.use('/graphql', yoga) + +export const server = app.listen(port, () => { + console.log(`Server listening at ${port}`) + console.info('API server runs on http://localhost:3001') + console.info('REST requests go through http://localhost:3001/api') + console.info('GraphQL requests go through http://localhost:3001/graphql') +}) diff --git a/packages/rfq-indexer/api/src/controllers/conflictingProofsController.ts b/packages/rfq-indexer/api/src/controllers/conflictingProofsController.ts new file mode 100644 index 0000000000..5079bb0d5f --- /dev/null +++ b/packages/rfq-indexer/api/src/controllers/conflictingProofsController.ts @@ -0,0 +1,50 @@ +import { Request, Response } from 'express' +import { sql } from 'kysely' + +import { db } from '../db' +import { qDeposits, qRelays, qProofs } from '../queries' +import { nest_results } from '../utils/nestResults' + +export const conflictingProofsController = async ( + req: Request, + res: Response +) => { + try { + const query = db + .with('deposits', () => qDeposits()) + .with('relays', () => qRelays()) + .with('proofs', () => qProofs()) + .with('combined', (qb) => + qb + .selectFrom('deposits') + .leftJoin('relays', 'transactionId_deposit', 'transactionId_relay') + .leftJoin('proofs', 'transactionId_deposit', 'transactionId_proof') + .selectAll('deposits') + .selectAll('relays') + .selectAll('proofs') + ) + .selectFrom('combined') + .selectAll() + .where('relayer_proof', 'is not', null) + .where('relayer_relay', 'is not', null) + .where( + (eb) => + sql`LOWER(${eb.ref('relayer_relay')}) != LOWER(${eb.ref( + 'relayer_proof' + )})` + ) + .orderBy('blockTimestamp_proof', 'desc') + + const results = await query.execute() + const conflictingProofs = nest_results(results) + + if (conflictingProofs && conflictingProofs.length > 0) { + res.json(conflictingProofs) + } else { + res.status(200).json({ message: 'No conflicting proofs found' }) + } + } catch (error) { + console.error('Error fetching conflicting proofs:', error) + res.status(500).json({ message: 'Internal server error' }) + } +} diff --git a/packages/rfq-indexer/api/src/controllers/invalidRelaysController.ts b/packages/rfq-indexer/api/src/controllers/invalidRelaysController.ts new file mode 100644 index 0000000000..4d17a56711 --- /dev/null +++ b/packages/rfq-indexer/api/src/controllers/invalidRelaysController.ts @@ -0,0 +1,52 @@ +import { Request, Response } from 'express' + +import { db } from '../db' + +export const recentInvalidRelaysController = async ( + req: Request, + res: Response +) => { + try { + const query = db + .selectFrom('BridgeRelayedEvents') + .leftJoin( + 'BridgeRequestEvents', + 'BridgeRelayedEvents.transactionId', + 'BridgeRequestEvents.transactionId' + ) + .select([ + 'BridgeRelayedEvents.transactionId', + 'BridgeRelayedEvents.blockNumber', + 'BridgeRelayedEvents.blockTimestamp', + 'BridgeRelayedEvents.transactionHash', + 'BridgeRelayedEvents.originChain', + 'BridgeRelayedEvents.destChain', + 'BridgeRelayedEvents.originChainId', + 'BridgeRelayedEvents.destChainId', + 'BridgeRelayedEvents.originToken', + 'BridgeRelayedEvents.destToken', + 'BridgeRelayedEvents.originAmountFormatted', + 'BridgeRelayedEvents.destAmountFormatted', + 'BridgeRelayedEvents.to', + 'BridgeRelayedEvents.relayer', + ]) + // lookback approx 2 weeks + .where( + 'BridgeRelayedEvents.blockTimestamp', + '>', + Math.floor(Date.now() / 1000) - 2 * 7 * 24 * 60 * 60 + ) + .where('BridgeRequestEvents.transactionId', 'is', null) + + const results = await query.execute() + + if (results && results.length > 0) { + res.json(results) + } else { + res.status(200).json({ message: 'No recent invalid relays found' }) + } + } catch (error) { + console.error('Error fetching recent invalid relays:', error) + res.status(500).json({ message: 'Internal server error' }) + } +} diff --git a/packages/rfq-indexer/api/src/controllers/pendingTransactionsController.ts b/packages/rfq-indexer/api/src/controllers/pendingTransactionsController.ts new file mode 100644 index 0000000000..dfef98c11e --- /dev/null +++ b/packages/rfq-indexer/api/src/controllers/pendingTransactionsController.ts @@ -0,0 +1,129 @@ +import { Request, Response } from 'express' + +import { db } from '../db' +import { qDeposits, qRelays, qProofs, qClaims, qRefunds } from '../queries' +import { nest_results } from '../utils/nestResults' + +export const pendingTransactionsMissingClaimController = async ( + req: Request, + res: Response +) => { + try { + const query = db + .with('deposits', () => qDeposits()) + .with('relays', () => qRelays()) + .with('proofs', () => qProofs()) + .with('claims', () => qClaims()) + .with('combined', (qb) => + qb + .selectFrom('deposits') + .innerJoin('relays', 'transactionId_deposit', 'transactionId_relay') + .innerJoin('proofs', 'transactionId_deposit', 'transactionId_proof') + .leftJoin('claims', 'transactionId_deposit', 'transactionId_claim') + .selectAll('deposits') + .selectAll('relays') + .selectAll('proofs') + .where('transactionId_claim', 'is', null) + ) + .selectFrom('combined') + .selectAll() + .orderBy('blockTimestamp_proof', 'desc') + + const results = await query.execute() + const nestedResults = nest_results(results) + + if (nestedResults && nestedResults.length > 0) { + res.json(nestedResults) + } else { + res + .status(404) + .json({ message: 'No pending transactions missing claim found' }) + } + } catch (error) { + console.error('Error fetching pending transactions missing claim:', error) + res.status(500).json({ message: 'Internal server error' }) + } +} + + +export const pendingTransactionsMissingProofController = async ( + req: Request, + res: Response +) => { + try { + const query = db + .with('deposits', () => qDeposits()) + .with('relays', () => qRelays()) + .with('proofs', () => qProofs()) + .with('combined', (qb) => + qb + .selectFrom('deposits') + .innerJoin('relays', 'transactionId_deposit', 'transactionId_relay') + .leftJoin('proofs', 'transactionId_deposit', 'transactionId_proof') + .selectAll('deposits') + .selectAll('relays') + .where('transactionId_proof', 'is', null) + ) + .selectFrom('combined') + .selectAll() + .orderBy('blockTimestamp_relay', 'desc') + + const results = await query.execute() + const nestedResults = nest_results(results) + + if (nestedResults && nestedResults.length > 0) { + res.json(nestedResults) + } else { + res + .status(404) + .json({ message: 'No pending transactions missing proof found' }) + } + } catch (error) { + console.error('Error fetching pending transactions missing proof:', error) + res.status(500).json({ message: 'Internal server error' }) + } +} + +export const pendingTransactionsMissingRelayController = async ( + req: Request, + res: Response +) => { + try { + const query = db + .with('deposits', () => qDeposits()) + .with('relays', () => qRelays()) + .with('refunds', () => qRefunds()) + .with( + 'combined', + (qb) => + qb + .selectFrom('deposits') + .selectAll('deposits') + .leftJoin('relays', 'transactionId_deposit', 'transactionId_relay') + .leftJoin( + 'refunds', + 'transactionId_deposit', + 'transactionId_refund' + ) + .where('transactionId_relay', 'is', null) // is not relayed + .where('transactionId_refund', 'is', null) // is not refunded + ) + .selectFrom('combined') + .selectAll() + .orderBy('blockTimestamp_deposit', 'desc') + + const results = await query.execute() + const nestedResults = nest_results(results) + + if (nestedResults && nestedResults.length > 0) { + res.json(nestedResults) + } else { + res + .status(404) + .json({ message: 'No pending transactions missing relay found' }) + } + } catch (error) { + console.error('Error fetching pending transactions missing relay:', error) + res.status(500).json({ message: 'Internal server error' }) + } +} diff --git a/packages/rfq-indexer/api/src/controllers/refundedAndRelayedController.ts b/packages/rfq-indexer/api/src/controllers/refundedAndRelayedController.ts new file mode 100644 index 0000000000..dd9fb2f0af --- /dev/null +++ b/packages/rfq-indexer/api/src/controllers/refundedAndRelayedController.ts @@ -0,0 +1,43 @@ +import { Request, Response } from 'express' + +import { db } from '../db' +import { qDeposits, qRelays, qRefunds } from '../queries' +import { nest_results } from '../utils/nestResults' + +export const refundedAndRelayedTransactionsController = async ( + req: Request, + res: Response +) => { + try { + const query = db + .with('deposits', () => qDeposits()) + .with('relays', () => qRelays()) + .with('refunds', () => qRefunds()) + .with('combined', (qb) => + qb + .selectFrom('deposits') + .innerJoin('relays', 'transactionId_deposit', 'transactionId_relay') + .innerJoin('refunds', 'transactionId_deposit', 'transactionId_refund') + .selectAll('deposits') + .selectAll('relays') + .selectAll('refunds') + ) + .selectFrom('combined') + .selectAll() + .orderBy('blockTimestamp_refund', 'desc') + + const results = await query.execute() + const nestedResults = nest_results(results) + + if (nestedResults && nestedResults.length > 0) { + res.json(nestedResults) + } else { + res + .status(200) + .json({ message: 'No refunded and relayed transactions found' }) + } + } catch (error) { + console.error('Error fetching refunded and relayed transactions:', error) + res.status(500).json({ message: 'Internal server error' }) + } +} diff --git a/packages/rfq-indexer/api/src/controllers/transactionIdController.ts b/packages/rfq-indexer/api/src/controllers/transactionIdController.ts new file mode 100644 index 0000000000..73857496fa --- /dev/null +++ b/packages/rfq-indexer/api/src/controllers/transactionIdController.ts @@ -0,0 +1,58 @@ +import { Request, Response } from 'express' + +import { db } from '../db' +import { qDeposits, qRelays, qProofs, qClaims, qRefunds } from '../queries' +import { nest_results } from '../utils/nestResults' + +export const getTransactionById = async (req: Request, res: Response) => { + const { transactionId } = req.params + + try { + const query = db + .with('deposits', () => + qDeposits().where('transactionId', '=', transactionId as string) + ) + .with('relays', () => qRelays()) + .with('proofs', () => qProofs()) + .with('claims', () => qClaims()) + .with('refunds', () => qRefunds()) + .with('combined', (qb) => + qb + .selectFrom('deposits') + .leftJoin('relays', 'transactionId_deposit', 'transactionId_relay') + .leftJoin('proofs', 'transactionId_deposit', 'transactionId_proof') + .leftJoin('claims', 'transactionId_deposit', 'transactionId_claim') + .leftJoin('refunds', 'transactionId_deposit', 'transactionId_refund') + .selectAll('deposits') + .selectAll('relays') + .selectAll('proofs') + .selectAll('claims') + .selectAll('refunds') + ) + .selectFrom('combined') + .selectAll() + + const results = await query.execute() + const nestedResult = nest_results(results)[0] || null + + if (nestedResult) { + const filteredResult = Object.fromEntries( + Object.entries(nestedResult).filter(([_, value]) => { + if (value === null) { + return false + } + if (typeof value !== 'object') { + return true + } + return Object.values(value).some((v) => v !== null) + }) + ) + res.json(filteredResult) + } else { + res.status(200).json({ message: 'Transaction not found' }) + } + } catch (error) { + console.error(error) + res.status(500).json({ message: 'Internal server error' }) + } +} diff --git a/packages/rfq-indexer/api/src/index.ts b/packages/rfq-indexer/api/src/index.ts deleted file mode 100644 index f64c412c84..0000000000 --- a/packages/rfq-indexer/api/src/index.ts +++ /dev/null @@ -1,100 +0,0 @@ -import express from 'express' -import { createYoga } from 'graphql-yoga' - -import { schema } from './graphql/schema' -import { overrideJsonBigIntSerialization } from './utils/overrideJsonBigIntSerialization' -import { resolvers } from './graphql/resolvers' - -overrideJsonBigIntSerialization() - -const app = express() - -const yoga = createYoga({ schema }) - -app.use(yoga.graphqlEndpoint, yoga) - -app.get('/api/hello', (req, res) => { - res.json({ message: 'Hello World!' }) -}) - -app.get('/api/pending-transactions-missing-relay', async (req, res) => { - try { - const pendingTransactions = - await resolvers.Query.pendingTransactionsMissingRelay() - res.json(pendingTransactions) - } catch (error) { - console.error('Error fetching pending transactions missing relay:', error) - res.status(500).json({ error: 'Internal server error' }) - } -}) - -app.get('/api/pending-transactions-missing-proof', async (req, res) => { - try { - const pendingTransactionsMissingProof = - await resolvers.Query.pendingTransactionsMissingProof() - res.json(pendingTransactionsMissingProof) - } catch (error) { - console.error('Error fetching pending transactions missing proof:', error) - res.status(500).json({ error: 'Internal server error' }) - } -}) - -app.get('/api/pending-transactions-missing-claim', async (req, res) => { - try { - const pendingTransactionsMissingClaim = - await resolvers.Query.pendingTransactionsMissingClaim() - res.json(pendingTransactionsMissingClaim) - } catch (error) { - console.error('Error fetching pending transactions missing claim:', error) - res.status(500).json({ error: 'Internal server error' }) - } -}) - -app.get('/api/recent-invalid-relays', async (req, res) => { - try { - const queryResult = await resolvers.Query.recentInvalidRelays() - res.json(queryResult) - } catch (error) { - console.error('Error fetching recent invalid relays:', error) - res.status(500).json({ error: 'Internal server error' }) - } -}) - -app.get('/api/conflicting-proofs', async (req, res) => { - try { - const conflictingProofs = await resolvers.Query.conflictingProofs() - res.json(conflictingProofs) - } catch (error) { - console.error('Error fetching conflicting proofs:', error) - res.status(500).json({ error: 'Internal server error' }) - } -}) - -app.get('/api/transaction/:transactionId', async (req, res) => { - try { - const transactionId = req.params.transactionId - const transaction = await resolvers.Query.transactionById(null, { - transactionId, - }) - res.json(transaction) - } catch (error) { - console.error('Error fetching transaction by ID:', error) - res.status(500).json({ error: 'Internal server error' }) - } -}) - -app.get('/api/refunded-and-relayed-transactions', async (req, res) => { - try { - const transactions = await resolvers.Query.refundedAndRelayedTransactions() - res.json(transactions) - } catch (error) { - console.error('Error fetching refunded and relayed transactions:', error) - res.status(500).json({ error: 'Internal server error' }) - } -}) - -app.listen(process.env.PORT, () => { - console.info('API server runs on http://localhost:3001') - console.info('REST requests go through http://localhost:3001/api') - console.info('GraphQL requests go through http://localhost:3001/graphql') -}) diff --git a/packages/rfq-indexer/api/src/jest.config.js b/packages/rfq-indexer/api/src/jest.config.js new file mode 100644 index 0000000000..ba447263ea --- /dev/null +++ b/packages/rfq-indexer/api/src/jest.config.js @@ -0,0 +1,10 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + roots: ['/src'], + transform: { + '^.+\\.(ts|tsx)$': 'babel-jest', + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], + moduleDirectories: ['node_modules', ''], +} diff --git a/packages/rfq-indexer/api/src/middleware/showFirstValidationError.ts b/packages/rfq-indexer/api/src/middleware/showFirstValidationError.ts new file mode 100644 index 0000000000..29ccda3e43 --- /dev/null +++ b/packages/rfq-indexer/api/src/middleware/showFirstValidationError.ts @@ -0,0 +1,24 @@ +import { Request, Response, NextFunction } from 'express' +import { validationResult } from 'express-validator' + +export const showFirstValidationError = ( + req: Request, + res: Response, + next: NextFunction +): void => { + const errors = validationResult(req) + if (!errors.isEmpty()) { + const firstError = errors.array({ onlyFirstError: true })[0] + + res.status(400).json({ + error: { + value: (firstError as any).value, + message: firstError?.msg, + field: firstError?.type === 'field' ? firstError?.path : undefined, + location: (firstError as any).location, + }, + }) + return + } + next() +} diff --git a/packages/rfq-indexer/api/src/queries/claimsQueries.ts b/packages/rfq-indexer/api/src/queries/claimsQueries.ts new file mode 100644 index 0000000000..8ce64a5e3e --- /dev/null +++ b/packages/rfq-indexer/api/src/queries/claimsQueries.ts @@ -0,0 +1,17 @@ +import { db } from '../db' + +// typical fields to return for a BridgeDepositClaimed event when it is joined to a BridgeRequest +export const qClaims = () => { + return db + .selectFrom('BridgeDepositClaimedEvents') + .select([ + 'BridgeDepositClaimedEvents.transactionId as transactionId_claim', + 'BridgeDepositClaimedEvents.blockNumber as blockNumber_claim', + 'BridgeDepositClaimedEvents.blockTimestamp as blockTimestamp_claim', + 'BridgeDepositClaimedEvents.transactionHash as transactionHash_claim', + + 'BridgeDepositClaimedEvents.to as to_claim', + 'BridgeDepositClaimedEvents.relayer as relayer_claim', + 'BridgeDepositClaimedEvents.amountFormatted as amountFormatted_claim', + ]) +} diff --git a/packages/rfq-indexer/api/src/queries/depositsQueries.ts b/packages/rfq-indexer/api/src/queries/depositsQueries.ts new file mode 100644 index 0000000000..61e33aa3d1 --- /dev/null +++ b/packages/rfq-indexer/api/src/queries/depositsQueries.ts @@ -0,0 +1,26 @@ +import { db } from '../db' + +export const qDeposits = () => { + return db + .selectFrom('BridgeRequestEvents') + .select([ + 'BridgeRequestEvents.transactionId as transactionId_deposit', + 'BridgeRequestEvents.blockNumber as blockNumber_deposit', + 'BridgeRequestEvents.blockTimestamp as blockTimestamp_deposit', + 'BridgeRequestEvents.transactionHash as transactionHash_deposit', + 'BridgeRequestEvents.originChain', + 'BridgeRequestEvents.destChain', + 'BridgeRequestEvents.originChainId', + 'BridgeRequestEvents.destChainId', + 'BridgeRequestEvents.originToken', + 'BridgeRequestEvents.destToken', + 'BridgeRequestEvents.originAmountFormatted', + 'BridgeRequestEvents.destAmountFormatted', + 'BridgeRequestEvents.sender', + 'BridgeRequestEvents.sendChainGas', + ]) + .where('BridgeRequestEvents.blockTimestamp', '>', 1722729600) + // if index is partially loaded, we must limit lookback or will have various data issues from relays + // that happened to be in flight at the point of the index's start. + // may also improve query performance +} diff --git a/packages/rfq-indexer/api/src/queries/index.ts b/packages/rfq-indexer/api/src/queries/index.ts new file mode 100644 index 0000000000..72bad2522e --- /dev/null +++ b/packages/rfq-indexer/api/src/queries/index.ts @@ -0,0 +1,5 @@ +export { qClaims } from './claimsQueries' +export { qDeposits } from './depositsQueries' +export { qProofs } from './proofsQueries' +export { qRefunds } from './refundsQueries' +export { qRelays } from './relaysQueries' diff --git a/packages/rfq-indexer/api/src/queries/proofsQueries.ts b/packages/rfq-indexer/api/src/queries/proofsQueries.ts new file mode 100644 index 0000000000..e7a1ccc012 --- /dev/null +++ b/packages/rfq-indexer/api/src/queries/proofsQueries.ts @@ -0,0 +1,15 @@ +import { db } from '../db' + +// typical fields to return for a BridgeProofProvided event when it is joined to a BridgeRequest +export const qProofs = () => { + return db + .selectFrom('BridgeProofProvidedEvents') + .select([ + 'BridgeProofProvidedEvents.transactionId as transactionId_proof', + 'BridgeProofProvidedEvents.blockNumber as blockNumber_proof', + 'BridgeProofProvidedEvents.blockTimestamp as blockTimestamp_proof', + 'BridgeProofProvidedEvents.transactionHash as transactionHash_proof', + + 'BridgeProofProvidedEvents.relayer as relayer_proof', + ]) +} diff --git a/packages/rfq-indexer/api/src/queries/refundsQueries.ts b/packages/rfq-indexer/api/src/queries/refundsQueries.ts new file mode 100644 index 0000000000..9de6f72773 --- /dev/null +++ b/packages/rfq-indexer/api/src/queries/refundsQueries.ts @@ -0,0 +1,15 @@ +import { db } from '../db' + +export const qRefunds = () => { + return db + .selectFrom('BridgeDepositRefundedEvents') + .select([ + 'BridgeDepositRefundedEvents.transactionId as transactionId_refund', + 'BridgeDepositRefundedEvents.blockNumber as blockNumber_refund', + 'BridgeDepositRefundedEvents.blockTimestamp as blockTimestamp_refund', + 'BridgeDepositRefundedEvents.transactionHash as transactionHash_refund', + + 'BridgeDepositRefundedEvents.to as to_refund', + 'BridgeDepositRefundedEvents.amountFormatted as amountFormatted_refund', + ]) +} diff --git a/packages/rfq-indexer/api/src/queries/relaysQueries.ts b/packages/rfq-indexer/api/src/queries/relaysQueries.ts new file mode 100644 index 0000000000..bd7b3990a0 --- /dev/null +++ b/packages/rfq-indexer/api/src/queries/relaysQueries.ts @@ -0,0 +1,16 @@ +import { db } from '../db' + +// typical fields to return for a BridgeRelayed event when it is joined to a BridgeRequest +export const qRelays = () => { + return db + .selectFrom('BridgeRelayedEvents') + .select([ + 'BridgeRelayedEvents.transactionId as transactionId_relay', + 'BridgeRelayedEvents.blockNumber as blockNumber_relay', + 'BridgeRelayedEvents.blockTimestamp as blockTimestamp_relay', + 'BridgeRelayedEvents.transactionHash as transactionHash_relay', + + 'BridgeRelayedEvents.relayer as relayer_relay', + 'BridgeRelayedEvents.to as to_relay', + ]) +} diff --git a/packages/rfq-indexer/api/src/routes/conflictingProofsRoute.ts b/packages/rfq-indexer/api/src/routes/conflictingProofsRoute.ts new file mode 100644 index 0000000000..aa48c89e07 --- /dev/null +++ b/packages/rfq-indexer/api/src/routes/conflictingProofsRoute.ts @@ -0,0 +1,65 @@ +import express from 'express' + +import { conflictingProofsController } from '../controllers/conflictingProofsController' + +const router = express.Router() + +/** + * @openapi + * /conflicting-proofs: + * get: + * summary: Get conflicting proofs + * description: Retrieves a list of transactions where the relayer in the proof differs from the relayer in the relay event + * responses: + * 200: + * description: Successful response + * content: + * application/json: + * schema: + * type: array + * items: + * type: object + * properties: + * Bridge: + * type: object + * description: General transaction fields + * BridgeRequest: + * type: object + * description: Deposit information + * BridgeRelay: + * type: object + * description: Relay information + * BridgeRefund: + * type: object + * description: Refund information + * BridgeProof: + * type: object + * description: Proof information (if available) + * BridgeClaim: + * type: object + * description: Claim information (if available) + * BridgeDispute: + * type: object + * description: Dispute information (if available) + * 404: + * description: No conflicting proofs found + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + */ +router.get('/', conflictingProofsController) + +export default router diff --git a/packages/rfq-indexer/api/src/routes/index.ts b/packages/rfq-indexer/api/src/routes/index.ts new file mode 100644 index 0000000000..302f878781 --- /dev/null +++ b/packages/rfq-indexer/api/src/routes/index.ts @@ -0,0 +1,17 @@ +import express from 'express' + +import pendingTransactionsRoute from './pendingTransactionsRoute' +import refundedAndRelayedRoute from './refundedAndRelayedRoute' +import invalidRelaysRoute from './invalidRelaysRoute' +import conflictingProofsRoute from './conflictingProofsRoute' +import transactionIdRoute from './transactionIdRoute' + +const router = express.Router() + +router.use('/pending-transactions', pendingTransactionsRoute) +router.use('/refunded-and-relayed', refundedAndRelayedRoute) +router.use('/invalid-relays', invalidRelaysRoute) +router.use('/conflicting-proofs', conflictingProofsRoute) +router.use('/transaction-id', transactionIdRoute) + +export default router diff --git a/packages/rfq-indexer/api/src/routes/invalidRelaysRoute.ts b/packages/rfq-indexer/api/src/routes/invalidRelaysRoute.ts new file mode 100644 index 0000000000..31356156f2 --- /dev/null +++ b/packages/rfq-indexer/api/src/routes/invalidRelaysRoute.ts @@ -0,0 +1,65 @@ +import express from 'express' + +import { recentInvalidRelaysController } from '../controllers/invalidRelaysController' + +const router = express.Router() + +/** + * @openapi + * /invalid-relays: + * get: + * summary: Get recent invalid relays + * description: Retrieves a list of recent invalid relay events from the past 2 weeks + * responses: + * 200: + * description: Successful response + * content: + * application/json: + * schema: + * type: array + * items: + * type: object + * properties: + * Bridge: + * type: object + * description: General transaction fields + * BridgeRequest: + * type: object + * description: Deposit information + * BridgeRelay: + * type: object + * description: Relay information + * BridgeRefund: + * type: object + * description: Refund information + * BridgeProof: + * type: object + * description: Proof information (if available) + * BridgeClaim: + * type: object + * description: Claim information (if available) + * BridgeDispute: + * type: object + * description: Dispute information (if available) + * 404: + * description: No recent invalid relays found + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + */ +router.get('/', recentInvalidRelaysController) + +export default router diff --git a/packages/rfq-indexer/api/src/routes/pendingTransactionsRoute.ts b/packages/rfq-indexer/api/src/routes/pendingTransactionsRoute.ts new file mode 100644 index 0000000000..2dbeafb121 --- /dev/null +++ b/packages/rfq-indexer/api/src/routes/pendingTransactionsRoute.ts @@ -0,0 +1,149 @@ +import express from 'express' + +import { + pendingTransactionsMissingClaimController, + pendingTransactionsMissingProofController, + pendingTransactionsMissingRelayController +} from '../controllers/pendingTransactionsController' + +const router = express.Router() + +/** + * @openapi + * /pending-transactions/missing-claim: + * get: + * summary: Get pending transactions missing claim + * description: Retrieves a list of transactions that have been deposited, relayed, and proven, but not yet claimed + * responses: + * 200: + * description: Successful response + * content: + * application/json: + * schema: + * type: array + * items: + * type: object + * properties: + * Bridge: + * type: object + * description: General transaction fields + * BridgeRequest: + * type: object + * description: Deposit information + * BridgeRelay: + * type: object + * description: Relay information + * BridgeRefund: + * type: object + * description: Refund information + * BridgeProof: + * type: object + * description: Proof information (if available) + * BridgeClaim: + * type: object + * description: Claim information (if available) + * BridgeDispute: + * type: object + * description: Dispute information (if available) + * 404: + * description: No pending transactions missing claim found + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + */ +router.get('/missing-claim', pendingTransactionsMissingClaimController) + +/** + * @openapi + * /pending-transactions/missing-proof: + * get: + * summary: Get pending transactions missing proof + * description: Retrieves a list of transactions that have been deposited and relayed, but not yet proven + * responses: + * 200: + * description: Successful response + * content: + * application/json: + * schema: + * type: array + * items: + * type: object + * properties: + * deposit: + * type: object + * relay: + * type: object + * 404: + * description: No pending transactions missing proof found + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + */ +router.get('/missing-proof', pendingTransactionsMissingProofController) + +/** + * @openapi + * /pending-transactions/missing-relay: + * get: + * summary: Get pending transactions missing relay + * description: Retrieves a list of transactions that have been deposited, but not yet relayed or refunded + * responses: + * 200: + * description: Successful response + * content: + * application/json: + * schema: + * type: array + * items: + * type: object + * properties: + * deposit: + * type: object + * 404: + * description: No pending transactions missing relay found + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + */ +router.get('/missing-relay', pendingTransactionsMissingRelayController) + +export default router diff --git a/packages/rfq-indexer/api/src/routes/refundedAndRelayedRoute.ts b/packages/rfq-indexer/api/src/routes/refundedAndRelayedRoute.ts new file mode 100644 index 0000000000..cd38b3b33e --- /dev/null +++ b/packages/rfq-indexer/api/src/routes/refundedAndRelayedRoute.ts @@ -0,0 +1,65 @@ +import express from 'express' + +import { refundedAndRelayedTransactionsController } from '../controllers/refundedAndRelayedController' + +const router = express.Router() + +/** + * @openapi + * /refunded-and-relayed: + * get: + * summary: Get refunded and relayed transactions + * description: Retrieves a list of transactions that have been both refunded and relayed + * responses: + * 200: + * description: Successful response + * content: + * application/json: + * schema: + * type: array + * items: + * type: object + * properties: + * Bridge: + * type: object + * description: General transaction fields + * BridgeRequest: + * type: object + * description: Deposit information + * BridgeRelay: + * type: object + * description: Relay information + * BridgeRefund: + * type: object + * description: Refund information + * BridgeProof: + * type: object + * description: Proof information (if available) + * BridgeClaim: + * type: object + * description: Claim information (if available) + * BridgeDispute: + * type: object + * description: Dispute information (if available) + * 404: + * description: No refunded and relayed transactions found + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + */ +router.get('/', refundedAndRelayedTransactionsController) + +export default router diff --git a/packages/rfq-indexer/api/src/routes/transactionIdRoute.ts b/packages/rfq-indexer/api/src/routes/transactionIdRoute.ts new file mode 100644 index 0000000000..ef0b4077a4 --- /dev/null +++ b/packages/rfq-indexer/api/src/routes/transactionIdRoute.ts @@ -0,0 +1,70 @@ +import express from 'express' + +import { getTransactionById } from '../controllers/transactionIdController' + +const router = express.Router() + +/** + * @openapi + * /transaction-id/{transactionId}: + * get: + * summary: Get transaction details by ID + * description: Retrieves detailed information about a transaction, including deposit, relay, proof, claim, and refund data if available + * parameters: + * - in: path + * name: transactionId + * required: true + * schema: + * type: string + * description: The unique identifier of the transaction + * responses: + * 200: + * description: Successful response + * content: + * application/json: + * schema: + * type: object + * properties: +* Bridge: +* type: object +* description: General transaction fields +* BridgeRequest: +* type: object +* description: Deposit information +* BridgeRelay: +* type: object +* description: Relay information +* BridgeRefund: +* type: object +* description: Refund information +* BridgeProof: +* type: object +* description: Proof information (if available) +* BridgeClaim: +* type: object +* description: Claim information (if available) +* BridgeDispute: +* type: object +* description: Dispute information (if available) + * 404: + * description: Transaction not found + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + */ +router.get('/:transactionId', getTransactionById) + +export default router diff --git a/packages/rfq-indexer/api/src/swagger.ts b/packages/rfq-indexer/api/src/swagger.ts new file mode 100644 index 0000000000..aec6d0fb43 --- /dev/null +++ b/packages/rfq-indexer/api/src/swagger.ts @@ -0,0 +1,28 @@ +import swaggerJsdoc from 'swagger-jsdoc' + +const isDevelopment = process.env.NODE_ENV === 'development' + +const devServer = { + url: 'http://localhost:3001/api', + description: 'Local Development Server', +} + +const prodServer = { + url: 'https://triumphant-magic-production.up.railway.app/api', + description: 'Production Server', +} + +const options: swaggerJsdoc.Options = { + definition: { + openapi: '3.0.0', + info: { + title: 'RFQ Indexer API', + version: '1.0.00', + description: 'API documentation for the RFQ Indexer API', + }, + servers: isDevelopment ? [devServer, prodServer] : [prodServer, devServer], + }, + apis: ['./src/routes/*.ts', './src/*.ts'], +} + +export const specs = swaggerJsdoc(options) diff --git a/packages/rfq-indexer/api/src/utils/isTransaction.ts b/packages/rfq-indexer/api/src/utils/isTransaction.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/rfq-indexer/api/src/utils/nestResults.ts b/packages/rfq-indexer/api/src/utils/nestResults.ts new file mode 100644 index 0000000000..6d31050362 --- /dev/null +++ b/packages/rfq-indexer/api/src/utils/nestResults.ts @@ -0,0 +1,58 @@ +export const nest_results = (sqlResults: any[]) => { + return sqlResults.map((transaction: any) => { + const bridgeRequest: { [key: string]: any } = {} + const bridgeRelay: { [key: string]: any } = {} + const bridgeProof: { [key: string]: any } = {} + const bridgeClaim: { [key: string]: any } = {} + const bridgeRefund: { [key: string]: any } = {} + const bridgeDispute: { [key: string]: any } = {} + const transactionFields: { [key: string]: any } = {} + + let transactionIdSet = false + + for (const [key, value] of Object.entries(transaction)) { + if (key.startsWith('transactionId')) { + if (!transactionIdSet) { + transactionFields[key.replace(/_.+$/, '')] = value + transactionIdSet = true + } + // Ignore other transactionId fields + } else if (key.endsWith('_deposit')) { + bridgeRequest[key.replace('_deposit', '')] = value + } else if (key.endsWith('_relay')) { + bridgeRelay[key.replace('_relay', '')] = value + } else if (key.endsWith('_proof')) { + bridgeProof[key.replace('_proof', '')] = value + } else if (key.endsWith('_claim')) { + bridgeClaim[key.replace('_claim', '')] = value + } else if (key.endsWith('_refund')) { + bridgeRefund[key.replace('_refund', '')] = value + } else if (key.endsWith('_dispute')) { + bridgeDispute[key.replace('_dispute', '')] = value + } else { + transactionFields[key] = value + } + } + + const result: { [key: string]: any } = { Bridge: transactionFields } + if (Object.keys(bridgeRequest).length) { + result.BridgeRequest = bridgeRequest + } + if (Object.keys(bridgeRelay).length) { + result.BridgeRelay = bridgeRelay + } + if (Object.keys(bridgeProof).length) { + result.BridgeProof = bridgeProof + } + if (Object.keys(bridgeClaim).length) { + result.BridgeClaim = bridgeClaim + } + if (Object.keys(bridgeRefund).length) { + result.BridgeRefund = bridgeRefund + } + if (Object.keys(bridgeDispute).length) { + result.BridgeDispute = bridgeDispute + } + return result + }) +} diff --git a/yarn.lock b/yarn.lock index 7c64ab096d..56fa13a4de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9992,12 +9992,12 @@ "@types/methods" "^1.1.4" "@types/superagent" "^8.1.0" -"@types/swagger-jsdoc@^6.0.4": +"@types/swagger-jsdoc@6.0.4", "@types/swagger-jsdoc@^6.0.4": version "6.0.4" resolved "https://registry.yarnpkg.com/@types/swagger-jsdoc/-/swagger-jsdoc-6.0.4.tgz#bb4f60f3a5f103818e022f2e29ff8935113fb83d" integrity sha512-W+Xw5epcOZrF/AooUM/PccNMSAFOKWZA5dasNyMujTwsBkU74njSJBpvCCJhHAJ95XRMzQrrW844Btu0uoetwQ== -"@types/swagger-ui-express@^4.1.6": +"@types/swagger-ui-express@4.1.6", "@types/swagger-ui-express@^4.1.6": version "4.1.6" resolved "https://registry.yarnpkg.com/@types/swagger-ui-express/-/swagger-ui-express-4.1.6.tgz#d0929e3fabac1a96a8a9c6c7ee8d42362c5cdf48" integrity sha512-UVSiGYXa5IzdJJG3hrc86e8KdZWLYxyEsVoUI4iPXc7CO4VZ3AfNP8d/8+hrDRIqz+HAaSMtZSqAsF3Nq2X/Dg== @@ -19383,6 +19383,15 @@ formidable@^2.1.2: once "^1.4.0" qs "^6.11.0" +formidable@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-3.5.1.tgz#9360a23a656f261207868b1484624c4c8d06ee1a" + integrity sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og== + dependencies: + dezalgo "^1.0.4" + hexoid "^1.0.0" + once "^1.4.0" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -34235,6 +34244,21 @@ superagent@^8.1.2: qs "^6.11.0" semver "^7.3.8" +superagent@^9.0.1: + version "9.0.2" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-9.0.2.tgz#a18799473fc57557289d6b63960610e358bdebc1" + integrity sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w== + dependencies: + component-emitter "^1.3.0" + cookiejar "^2.1.4" + debug "^4.3.4" + fast-safe-stringify "^2.1.1" + form-data "^4.0.0" + formidable "^3.5.1" + methods "^1.1.2" + mime "2.6.0" + qs "^6.11.0" + superstruct@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-1.0.4.tgz#0adb99a7578bd2f1c526220da6571b2d485d91ca" @@ -34248,6 +34272,14 @@ supertest@^6.3.3: methods "^1.1.2" superagent "^8.1.2" +supertest@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/supertest/-/supertest-7.0.0.tgz#cac53b3d6872a0b317980b2b0cfa820f09cd7634" + integrity sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA== + dependencies: + methods "^1.1.2" + superagent "^9.0.1" + supports-color@8.1.1, supports-color@^8.0.0, supports-color@^8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" From 4cb5d73960d1e89ce75c8105facd9e2ac33cb46d Mon Sep 17 00:00:00 2001 From: Defi-Moses Date: Sun, 22 Sep 2024 19:03:57 +0000 Subject: [PATCH 10/90] Publish - @synapsecns/rfq-indexer-api@1.0.4 --- packages/rfq-indexer/api/CHANGELOG.md | 8 ++++++++ packages/rfq-indexer/api/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/rfq-indexer/api/CHANGELOG.md b/packages/rfq-indexer/api/CHANGELOG.md index 748a06cf8b..2ffd88a1c8 100644 --- a/packages/rfq-indexer/api/CHANGELOG.md +++ b/packages/rfq-indexer/api/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.0.4](https://github.com/synapsecns/sanguine/compare/@synapsecns/rfq-indexer-api@1.0.3...@synapsecns/rfq-indexer-api@1.0.4) (2024-09-22) + +**Note:** Version bump only for package @synapsecns/rfq-indexer-api + + + + + ## [1.0.3](https://github.com/synapsecns/sanguine/compare/@synapsecns/rfq-indexer-api@1.0.2...@synapsecns/rfq-indexer-api@1.0.3) (2024-09-20) **Note:** Version bump only for package @synapsecns/rfq-indexer-api diff --git a/packages/rfq-indexer/api/package.json b/packages/rfq-indexer/api/package.json index 534967fbf3..ca054661f8 100644 --- a/packages/rfq-indexer/api/package.json +++ b/packages/rfq-indexer/api/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/rfq-indexer-api", "private": true, - "version": "1.0.3", + "version": "1.0.4", "description": "", "main": "index.js", "scripts": { From 1891fed48ae2ab0028333ca09338eb175d5d3319 Mon Sep 17 00:00:00 2001 From: Moses <103143573+Defi-Moses@users.noreply.github.com> Date: Sun, 22 Sep 2024 21:20:58 +0100 Subject: [PATCH 11/90] fix read mes (#3168) --- README.md | 1 + packages/rfq-indexer/README.md | 23 ++++++++++++----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 35bfaeef94..4a9b769013 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ root │ ├── coverage-aggregator: Javascript coverage aggregator based on nyc │ ├── docs: Docasaurus documentation. Note: this is not yet in use, and docs are still maintained on gitbook │ ├── explorer-ui: Explorer UI +│ ├── rfq-indexer: RFQ indexer │ ├── rest-api: Rest API │ ├── sdk-router: SDK router │ ├── solidity-devops: provides a set of tools and scripts to help with the development of Solidity smart contracts diff --git a/packages/rfq-indexer/README.md b/packages/rfq-indexer/README.md index 120c08c78d..6d70eb8e37 100644 --- a/packages/rfq-indexer/README.md +++ b/packages/rfq-indexer/README.md @@ -12,14 +12,15 @@ The RFQ (Request for Quote) Indexer is a system designed to index and track brid - API: Used by front-end applications, other services, or developers to query the indexed data. ## Directory Structure -rfq-indexer/ -├── api/ # API service -│ ├── src/ # API source code -│ ├── package.json # API dependencies and scripts -│ └── README.md # API documentation -├── indexer/ # Indexer service -│ ├── src/ # Indexer source code -│ ├── abis/ # Contract ABIs -│ ├── package.json # Indexer dependencies and scripts -│ └── README.md # Indexer documentation -└── README.md # This file +
+rfq-indexer
+├── api: API service
+│   ├── src/ : API source code
+│   ├── package.json : API dependencies and scripts
+│   ├── README.md : API documentation
+├── indexer: Indexer service
+│   ├── src/ : Indexer source code
+│   ├── abis/ : Contract ABIs
+│   ├── package.json : Indexer dependencies and scripts
+│   ├── README.md : Indexer documentation
+
From faf93873a7b8931a5fd5a923be9227f26e32c168 Mon Sep 17 00:00:00 2001 From: ChiTimesChi Date: Mon, 23 Sep 2024 12:28:49 +0000 Subject: [PATCH 12/90] Publish - @synapsecns/contracts-core@1.0.32 - FastBridge@0.4.1 - @synapsecns/solidity-devops@0.4.5 --- packages/contracts-core/CHANGELOG.md | 8 ++++++++ packages/contracts-core/package.json | 2 +- packages/contracts-rfq/CHANGELOG.md | 8 ++++++++ packages/contracts-rfq/package.json | 4 ++-- packages/solidity-devops/CHANGELOG.md | 8 ++++++++ packages/solidity-devops/package.json | 2 +- 6 files changed, 28 insertions(+), 4 deletions(-) diff --git a/packages/contracts-core/CHANGELOG.md b/packages/contracts-core/CHANGELOG.md index 7f396cd407..83c627a3fe 100644 --- a/packages/contracts-core/CHANGELOG.md +++ b/packages/contracts-core/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.0.32](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-core@1.0.31...@synapsecns/contracts-core@1.0.32) (2024-09-23) + +**Note:** Version bump only for package @synapsecns/contracts-core + + + + + ## [1.0.31](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-core@1.0.30...@synapsecns/contracts-core@1.0.31) (2024-04-03) **Note:** Version bump only for package @synapsecns/contracts-core diff --git a/packages/contracts-core/package.json b/packages/contracts-core/package.json index 495546cd2c..48dd32ba5a 100644 --- a/packages/contracts-core/package.json +++ b/packages/contracts-core/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/contracts-core", - "version": "1.0.31", + "version": "1.0.32", "description": "", "scripts": { "build": "yarn build:contracts && yarn build:typescript && yarn build:go", diff --git a/packages/contracts-rfq/CHANGELOG.md b/packages/contracts-rfq/CHANGELOG.md index d0f588aa52..daa73da3e7 100644 --- a/packages/contracts-rfq/CHANGELOG.md +++ b/packages/contracts-rfq/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.1](https://github.com/synapsecns/sanguine/compare/FastBridge@0.4.0...FastBridge@0.4.1) (2024-09-23) + +**Note:** Version bump only for package FastBridge + + + + + # [0.4.0](https://github.com/synapsecns/sanguine/compare/FastBridge@0.3.0...FastBridge@0.4.0) (2024-09-20) diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index 0cd9cb469a..44666d9fc7 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,7 +1,7 @@ { "name": "FastBridge", "license": "UNLICENSED", - "version": "0.4.0", + "version": "0.4.1", "description": "FastBridge contracts.", "private": true, "files": [ @@ -11,7 +11,7 @@ "@openzeppelin/contracts": "5.0.1" }, "devDependencies": { - "@synapsecns/solidity-devops": "^0.4.4", + "@synapsecns/solidity-devops": "^0.4.5", "prettier": "^2.5.1", "prettier-plugin-solidity": "^1.0.0-beta.19", "solhint": "^3.3.6" diff --git a/packages/solidity-devops/CHANGELOG.md b/packages/solidity-devops/CHANGELOG.md index 38e5edb4bf..b7149765ba 100644 --- a/packages/solidity-devops/CHANGELOG.md +++ b/packages/solidity-devops/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.5](https://github.com/synapsecns/sanguine/compare/@synapsecns/solidity-devops@0.4.4...@synapsecns/solidity-devops@0.4.5) (2024-09-23) + +**Note:** Version bump only for package @synapsecns/solidity-devops + + + + + ## [0.4.4](https://github.com/synapsecns/sanguine/compare/@synapsecns/solidity-devops@0.4.3...@synapsecns/solidity-devops@0.4.4) (2024-07-17) diff --git a/packages/solidity-devops/package.json b/packages/solidity-devops/package.json index 713845fae2..3819d952b1 100644 --- a/packages/solidity-devops/package.json +++ b/packages/solidity-devops/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/solidity-devops", - "version": "0.4.4", + "version": "0.4.5", "description": "A collection of utils to effortlessly test, deploy and maintain the smart contracts on EVM compatible blockchains", "license": "MIT", "repository": { From 8c702c794b039450dec650e2b5b63095574b073d Mon Sep 17 00:00:00 2001 From: vro <168573323+golangisfun123@users.noreply.github.com> Date: Mon, 23 Sep 2024 11:30:11 -0500 Subject: [PATCH 13/90] fix(opbot): use submitter get tx status [SLT-158] (#3134) * use experimental logger to debug * fix lint * [goreleaser] * use submitter instead of client * [goreleaser] * [goreleaser] --- contrib/opbot/botmd/commands.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/contrib/opbot/botmd/commands.go b/contrib/opbot/botmd/commands.go index 5dc95b8403..b72f4ff7c2 100644 --- a/contrib/opbot/botmd/commands.go +++ b/contrib/opbot/botmd/commands.go @@ -25,6 +25,7 @@ import ( "github.com/synapsecns/sanguine/core/retry" "github.com/synapsecns/sanguine/ethergo/chaindata" "github.com/synapsecns/sanguine/ethergo/client" + "github.com/synapsecns/sanguine/ethergo/submitter" rfqClient "github.com/synapsecns/sanguine/services/rfq/api/client" "github.com/synapsecns/sanguine/services/rfq/contracts/fastbridge" "github.com/synapsecns/sanguine/services/rfq/relayer/relapi" @@ -347,17 +348,12 @@ func (b *Bot) rfqRefund() *slacker.CommandDefinition { return } - var txHash *relapi.TxHashByNonceResponse + var status submitter.SubmissionStatus err = retry.WithBackoff( ctx.Context(), func(ctx context.Context) error { - txHash, err = relClient.GetTxHashByNonce( - ctx, - &relapi.GetTxByNonceRequest{ - ChainID: rawRequest.OriginChainID, - Nonce: nonce, - }) - if err != nil { + status, err = b.submitter.GetSubmissionStatus(ctx, big.NewInt(int64(rawRequest.OriginChainID)), nonce) + if err != nil || !status.HasTx() { b.logger.Errorf(ctx, "error fetching quote request: %v", err) return fmt.Errorf("error fetching quote request: %w", err) } @@ -373,7 +369,7 @@ func (b *Bot) rfqRefund() *slacker.CommandDefinition { return } - _, err = ctx.Response().Reply(fmt.Sprintf("refund submitted: %s", toExplorerSlackLink(txHash.Hash))) + _, err = ctx.Response().Reply(fmt.Sprintf("refund submitted: %s", toExplorerSlackLink(status.TxHash().String()))) if err != nil { log.Println(err) } From 9418b40aa25a441d6a4460695962f7fbf41c4221 Mon Sep 17 00:00:00 2001 From: abtestingalpha <104046418+abtestingalpha@users.noreply.github.com> Date: Mon, 23 Sep 2024 12:53:33 -0400 Subject: [PATCH 14/90] fix(synapse-interface): Additional checks on screen [SLT-166] (#3152) * Additional checks on screen * Adds checks on chain/token changes --- .../components/ui/ChainSelector.tsx | 2 +- .../contexts/UserProvider.tsx | 16 +- .../pages/state-managed-bridge/index.tsx | 14 +- .../synapse-interface/pages/swap/index.tsx | 14 +- .../synapse-interface/utils/screenAddress.ts | 20 +- yarn.lock | 643 +----------------- 6 files changed, 53 insertions(+), 656 deletions(-) diff --git a/packages/synapse-interface/components/ui/ChainSelector.tsx b/packages/synapse-interface/components/ui/ChainSelector.tsx index 3eee869fc6..87dab012d6 100644 --- a/packages/synapse-interface/components/ui/ChainSelector.tsx +++ b/packages/synapse-interface/components/ui/ChainSelector.tsx @@ -45,7 +45,7 @@ export function ChainSelector({ newToChainId: chainId, } - segmentAnalyticsEvent(eventTitle, eventData) + segmentAnalyticsEvent(eventTitle, eventData, true) dispatch(setFunction(chainId)) } } diff --git a/packages/synapse-interface/contexts/UserProvider.tsx b/packages/synapse-interface/contexts/UserProvider.tsx index 6476d8e19e..82323c3402 100644 --- a/packages/synapse-interface/contexts/UserProvider.tsx +++ b/packages/synapse-interface/contexts/UserProvider.tsx @@ -21,12 +21,16 @@ export const UserProvider = ({ children }) => { useAccountEffect({ onConnect() { - segmentAnalyticsEvent(`[Wallet Analytics] connects`, { - walletId: connector?.id, - chainId: chain?.id, - query, - pathname, - }) + segmentAnalyticsEvent( + `[Wallet Analytics] connects`, + { + walletId: connector?.id, + chainId: chain?.id, + query, + pathname, + }, + true + ) }, onDisconnect() { segmentAnalyticsEvent('[Wallet Analytics] disconnect', {}) diff --git a/packages/synapse-interface/pages/state-managed-bridge/index.tsx b/packages/synapse-interface/pages/state-managed-bridge/index.tsx index 87f503de4f..31a04528f0 100644 --- a/packages/synapse-interface/pages/state-managed-bridge/index.tsx +++ b/packages/synapse-interface/pages/state-managed-bridge/index.tsx @@ -110,11 +110,15 @@ const StateManagedBridge = () => { } = useMaintenance() useEffect(() => { - segmentAnalyticsEvent(`[Bridge page] arrives`, { - fromChainId, - query, - pathname, - }) + segmentAnalyticsEvent( + `[Bridge page] arrives`, + { + fromChainId, + query, + pathname, + }, + true + ) }, [query]) useEffect(() => { diff --git a/packages/synapse-interface/pages/swap/index.tsx b/packages/synapse-interface/pages/swap/index.tsx index 4063348d2b..f3462cc76d 100644 --- a/packages/synapse-interface/pages/swap/index.tsx +++ b/packages/synapse-interface/pages/swap/index.tsx @@ -90,11 +90,15 @@ const StateManagedSwap = () => { const dispatch = useAppDispatch() useEffect(() => { - segmentAnalyticsEvent(`[Swap page] arrives`, { - swapChainId, - query, - pathname, - }) + segmentAnalyticsEvent( + `[Swap page] arrives`, + { + swapChainId, + query, + pathname, + }, + true + ) }, [query]) useEffect(() => { diff --git a/packages/synapse-interface/utils/screenAddress.ts b/packages/synapse-interface/utils/screenAddress.ts index 5f383a94e5..45e7aaa9c9 100644 --- a/packages/synapse-interface/utils/screenAddress.ts +++ b/packages/synapse-interface/utils/screenAddress.ts @@ -12,15 +12,18 @@ const createRiskDetectedEvent = (address: Address | string) => { }) } +const dispatchRiskDetectedEvent = (address: Address | string) => { + const event = createRiskDetectedEvent(address) + GlobalEventEmitter.dispatchEvent(event) +} + export const screenAddress = async ( address: Address | string ): Promise => { const url = `https://screener.omnirpc.io/fe/address/${address}` if (isBlacklisted(address)) { - const event = createRiskDetectedEvent(address) - - GlobalEventEmitter.dispatchEvent(event) + dispatchRiskDetectedEvent(address) return true } @@ -29,14 +32,13 @@ export const screenAddress = async ( const { risk } = await response.json() if (risk) { - const event = createRiskDetectedEvent(address) - - GlobalEventEmitter.dispatchEvent(event) + dispatchRiskDetectedEvent(address) return true + } else { + return false } - return false } catch (error) { - console.error('Error:', error) - return false + dispatchRiskDetectedEvent(address) + return true } } diff --git a/yarn.lock b/yarn.lock index 56fa13a4de..39e01c8b37 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1523,7 +1523,7 @@ "@babel/parser" "^7.25.0" "@babel/types" "^7.25.0" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.11.5", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.22.8", "@babel/traverse@^7.23.2", "@babel/traverse@^7.24.7", "@babel/traverse@^7.24.8", "@babel/traverse@^7.25.0", "@babel/traverse@^7.25.1", "@babel/traverse@^7.25.2", "@babel/traverse@^7.25.3", "@babel/traverse@^7.25.4", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2": +"@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.11.5", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.22.8", "@babel/traverse@^7.23.2", "@babel/traverse@^7.24.7", "@babel/traverse@^7.24.8", "@babel/traverse@^7.25.0", "@babel/traverse@^7.25.1", "@babel/traverse@^7.25.2", "@babel/traverse@^7.25.3", "@babel/traverse@^7.25.4", "@babel/traverse@^7.7.0": version "7.25.6" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.6.tgz#04fad980e444f182ecf1520504941940a90fea41" integrity sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ== @@ -4383,18 +4383,6 @@ jest-util "^25.5.0" slash "^3.0.0" -"@jest/console@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-28.1.3.tgz#2030606ec03a18c31803b8a36382762e447655df" - integrity sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw== - dependencies: - "@jest/types" "^28.1.3" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - slash "^3.0.0" - "@jest/console@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" @@ -4441,41 +4429,6 @@ slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/core@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-28.1.3.tgz#0ebf2bd39840f1233cd5f2d1e6fc8b71bd5a1ac7" - integrity sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA== - dependencies: - "@jest/console" "^28.1.3" - "@jest/reporters" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - ci-info "^3.2.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^28.1.3" - jest-config "^28.1.3" - jest-haste-map "^28.1.3" - jest-message-util "^28.1.3" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-resolve-dependencies "^28.1.3" - jest-runner "^28.1.3" - jest-runtime "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" - jest-watcher "^28.1.3" - micromatch "^4.0.4" - pretty-format "^28.1.3" - rimraf "^3.0.0" - slash "^3.0.0" - strip-ansi "^6.0.0" - "@jest/core@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" @@ -4519,16 +4472,6 @@ "@jest/types" "^25.5.0" jest-mock "^25.5.0" -"@jest/environment@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-28.1.3.tgz#abed43a6b040a4c24fdcb69eab1f97589b2d663e" - integrity sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA== - dependencies: - "@jest/fake-timers" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - jest-mock "^28.1.3" - "@jest/environment@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" @@ -4539,13 +4482,6 @@ "@types/node" "*" jest-mock "^29.7.0" -"@jest/expect-utils@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" - integrity sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA== - dependencies: - jest-get-type "^28.0.2" - "@jest/expect-utils@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" @@ -4553,14 +4489,6 @@ dependencies: jest-get-type "^29.6.3" -"@jest/expect@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-28.1.3.tgz#9ac57e1d4491baca550f6bdbd232487177ad6a72" - integrity sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw== - dependencies: - expect "^28.1.3" - jest-snapshot "^28.1.3" - "@jest/expect@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" @@ -4580,18 +4508,6 @@ jest-util "^25.5.0" lolex "^5.0.0" -"@jest/fake-timers@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-28.1.3.tgz#230255b3ad0a3d4978f1d06f70685baea91c640e" - integrity sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw== - dependencies: - "@jest/types" "^28.1.3" - "@sinonjs/fake-timers" "^9.1.2" - "@types/node" "*" - jest-message-util "^28.1.3" - jest-mock "^28.1.3" - jest-util "^28.1.3" - "@jest/fake-timers@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" @@ -4613,15 +4529,6 @@ "@jest/types" "^25.5.0" expect "^25.5.0" -"@jest/globals@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-28.1.3.tgz#a601d78ddc5fdef542728309894895b4a42dc333" - integrity sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA== - dependencies: - "@jest/environment" "^28.1.3" - "@jest/expect" "^28.1.3" - "@jest/types" "^28.1.3" - "@jest/globals@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" @@ -4664,37 +4571,6 @@ optionalDependencies: node-notifier "^6.0.0" -"@jest/reporters@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-28.1.3.tgz#9adf6d265edafc5fc4a434cfb31e2df5a67a369a" - integrity sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@jridgewell/trace-mapping" "^0.3.13" - "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^5.1.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - jest-worker "^28.1.3" - slash "^3.0.0" - string-length "^4.0.1" - strip-ansi "^6.0.0" - terminal-link "^2.0.0" - v8-to-istanbul "^9.0.1" - "@jest/reporters@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" @@ -4725,13 +4601,6 @@ strip-ansi "^6.0.0" v8-to-istanbul "^9.0.1" -"@jest/schemas@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" - integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg== - dependencies: - "@sinclair/typebox" "^0.24.1" - "@jest/schemas@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" @@ -4748,15 +4617,6 @@ graceful-fs "^4.2.4" source-map "^0.6.0" -"@jest/source-map@^28.1.2": - version "28.1.2" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-28.1.2.tgz#7fe832b172b497d6663cdff6c13b0a920e139e24" - integrity sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww== - dependencies: - "@jridgewell/trace-mapping" "^0.3.13" - callsites "^3.0.0" - graceful-fs "^4.2.9" - "@jest/source-map@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" @@ -4776,16 +4636,6 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-result@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.3.tgz#5eae945fd9f4b8fcfce74d239e6f725b6bf076c5" - integrity sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg== - dependencies: - "@jest/console" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - "@jest/test-result@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" @@ -4807,16 +4657,6 @@ jest-runner "^25.5.4" jest-runtime "^25.5.4" -"@jest/test-sequencer@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz#9d0c283d906ac599c74bde464bc0d7e6a82886c3" - integrity sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw== - dependencies: - "@jest/test-result" "^28.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - slash "^3.0.0" - "@jest/test-sequencer@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" @@ -4870,27 +4710,6 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/transform@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-28.1.3.tgz#59d8098e50ab07950e0f2fc0fc7ec462371281b0" - integrity sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA== - dependencies: - "@babel/core" "^7.11.6" - "@jest/types" "^28.1.3" - "@jridgewell/trace-mapping" "^0.3.13" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-regex-util "^28.0.2" - jest-util "^28.1.3" - micromatch "^4.0.4" - pirates "^4.0.4" - slash "^3.0.0" - write-file-atomic "^4.0.1" - "@jest/transform@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" @@ -4953,18 +4772,6 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jest/types@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b" - integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ== - dependencies: - "@jest/schemas" "^28.1.3" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - "@jest/types@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" @@ -5017,7 +4824,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== @@ -7507,11 +7314,6 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== -"@sinclair/typebox@^0.24.1": - version "0.24.51" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" - integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== - "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -7558,13 +7360,6 @@ dependencies: "@sinonjs/commons" "^3.0.0" -"@sinonjs/fake-timers@^9.1.2": - version "9.1.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" - integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== - dependencies: - "@sinonjs/commons" "^1.7.0" - "@slorber/remark-comment@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@slorber/remark-comment/-/remark-comment-1.0.0.tgz#2a020b3f4579c89dec0361673206c28d67e08f5a" @@ -8901,6 +8696,14 @@ "@swc/counter" "^0.1.3" tslib "^2.4.0" +"@synapsecns/coverage-aggregator@file:./packages/coverage-aggregator": + version "1.0.6" + dependencies: + glob "^8.0.3" + path "^0.12.7" + ts-jest "^29.0.5" + yargs "^17.6.2" + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -9785,7 +9588,7 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== -"@types/prettier@^2.1.1", "@types/prettier@^2.1.5": +"@types/prettier@^2.1.1": version "2.7.3" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== @@ -12380,19 +12183,6 @@ babel-jest@^25.2.6, babel-jest@^25.5.1: graceful-fs "^4.2.4" slash "^3.0.0" -babel-jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-28.1.3.tgz#c1187258197c099072156a0a121c11ee1e3917d5" - integrity sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q== - dependencies: - "@jest/transform" "^28.1.3" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^28.1.3" - chalk "^4.0.0" - graceful-fs "^4.2.9" - slash "^3.0.0" - babel-jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" @@ -12497,16 +12287,6 @@ babel-plugin-jest-hoist@^25.5.0: "@babel/types" "^7.3.3" "@types/babel__traverse" "^7.0.6" -babel-plugin-jest-hoist@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz#1952c4d0ea50f2d6d794353762278d1d8cca3fbe" - integrity sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.1.14" - "@types/babel__traverse" "^7.0.6" - babel-plugin-jest-hoist@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" @@ -12702,14 +12482,6 @@ babel-preset-jest@^25.5.0: babel-plugin-jest-hoist "^25.5.0" babel-preset-current-node-syntax "^0.1.2" -babel-preset-jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz#5dfc20b99abed5db994406c2b9ab94c73aaa419d" - integrity sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A== - dependencies: - babel-plugin-jest-hoist "^28.1.3" - babel-preset-current-node-syntax "^1.0.0" - babel-preset-jest@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" @@ -16225,11 +15997,6 @@ diff-sequences@^25.2.6: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg== -diff-sequences@^28.1.1: - version "28.1.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" - integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw== - diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" @@ -16718,11 +16485,6 @@ elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4, elliptic@^6. minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -emittery@^0.10.2: - version "0.10.2" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" - integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== - emittery@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" @@ -18599,17 +18361,6 @@ expect@^25.5.0: jest-message-util "^25.5.0" jest-regex-util "^25.2.6" -expect@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec" - integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g== - dependencies: - "@jest/expect-utils" "^28.1.3" - jest-get-type "^28.0.2" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - expect@^29.0.0, expect@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" @@ -22604,7 +22355,7 @@ istanbul-lib-instrument@^4.0.0: istanbul-lib-coverage "^3.0.0" semver "^6.3.0" -istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: +istanbul-lib-instrument@^5.0.4: version "5.2.1" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== @@ -22724,14 +22475,6 @@ jest-changed-files@^25.5.0: execa "^3.2.0" throat "^5.0.0" -jest-changed-files@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-28.1.3.tgz#d9aeee6792be3686c47cb988a8eaf82ff4238831" - integrity sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA== - dependencies: - execa "^5.0.0" - p-limit "^3.1.0" - jest-changed-files@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" @@ -22741,31 +22484,6 @@ jest-changed-files@^29.7.0: jest-util "^29.7.0" p-limit "^3.1.0" -jest-circus@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-28.1.3.tgz#d14bd11cf8ee1a03d69902dc47b6bd4634ee00e4" - integrity sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow== - dependencies: - "@jest/environment" "^28.1.3" - "@jest/expect" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^0.7.0" - is-generator-fn "^2.0.0" - jest-each "^28.1.3" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-runtime "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" - p-limit "^3.1.0" - pretty-format "^28.1.3" - slash "^3.0.0" - stack-utils "^2.0.3" - jest-circus@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" @@ -22812,24 +22530,6 @@ jest-cli@^25.5.4: realpath-native "^2.0.0" yargs "^15.3.1" -jest-cli@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-28.1.3.tgz#558b33c577d06de55087b8448d373b9f654e46b2" - integrity sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ== - dependencies: - "@jest/core" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - import-local "^3.0.2" - jest-config "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" - prompts "^2.0.1" - yargs "^17.3.1" - jest-cli@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" @@ -22872,34 +22572,6 @@ jest-config@^25.5.4: pretty-format "^25.5.0" realpath-native "^2.0.0" -jest-config@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-28.1.3.tgz#e315e1f73df3cac31447eed8b8740a477392ec60" - integrity sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ== - dependencies: - "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^28.1.3" - "@jest/types" "^28.1.3" - babel-jest "^28.1.3" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-circus "^28.1.3" - jest-environment-node "^28.1.3" - jest-get-type "^28.0.2" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-runner "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" - micromatch "^4.0.4" - parse-json "^5.2.0" - pretty-format "^28.1.3" - slash "^3.0.0" - strip-json-comments "^3.1.1" - jest-config@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" @@ -22948,16 +22620,6 @@ jest-diff@^25.2.1, jest-diff@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" -jest-diff@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f" - integrity sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw== - dependencies: - chalk "^4.0.0" - diff-sequences "^28.1.1" - jest-get-type "^28.0.2" - pretty-format "^28.1.3" - jest-diff@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" @@ -22975,13 +22637,6 @@ jest-docblock@^25.3.0: dependencies: detect-newline "^3.0.0" -jest-docblock@^28.1.1: - version "28.1.1" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-28.1.1.tgz#6f515c3bf841516d82ecd57a62eed9204c2f42a8" - integrity sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA== - dependencies: - detect-newline "^3.0.0" - jest-docblock@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" @@ -23000,17 +22655,6 @@ jest-each@^25.5.0: jest-util "^25.5.0" pretty-format "^25.5.0" -jest-each@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-28.1.3.tgz#bdd1516edbe2b1f3569cfdad9acd543040028f81" - integrity sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g== - dependencies: - "@jest/types" "^28.1.3" - chalk "^4.0.0" - jest-get-type "^28.0.2" - jest-util "^28.1.3" - pretty-format "^28.1.3" - jest-each@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" @@ -23046,18 +22690,6 @@ jest-environment-node@^25.5.0: jest-util "^25.5.0" semver "^6.3.0" -jest-environment-node@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-28.1.3.tgz#7e74fe40eb645b9d56c0c4b70ca4357faa349be5" - integrity sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A== - dependencies: - "@jest/environment" "^28.1.3" - "@jest/fake-timers" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - jest-mock "^28.1.3" - jest-util "^28.1.3" - jest-environment-node@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" @@ -23088,11 +22720,6 @@ jest-get-type@^25.2.6: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig== -jest-get-type@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" - integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== - jest-get-type@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" @@ -23139,25 +22766,6 @@ jest-haste-map@^26.6.2: optionalDependencies: fsevents "^2.1.2" -jest-haste-map@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-28.1.3.tgz#abd5451129a38d9841049644f34b034308944e2b" - integrity sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA== - dependencies: - "@jest/types" "^28.1.3" - "@types/graceful-fs" "^4.1.3" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.9" - jest-regex-util "^28.0.2" - jest-util "^28.1.3" - jest-worker "^28.1.3" - micromatch "^4.0.4" - walker "^1.0.8" - optionalDependencies: - fsevents "^2.3.2" - jest-haste-map@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" @@ -23208,14 +22816,6 @@ jest-leak-detector@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" -jest-leak-detector@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz#a6685d9b074be99e3adee816ce84fd30795e654d" - integrity sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA== - dependencies: - jest-get-type "^28.0.2" - pretty-format "^28.1.3" - jest-leak-detector@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" @@ -23234,16 +22834,6 @@ jest-matcher-utils@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" -jest-matcher-utils@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e" - integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw== - dependencies: - chalk "^4.0.0" - jest-diff "^28.1.3" - jest-get-type "^28.0.2" - pretty-format "^28.1.3" - jest-matcher-utils@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" @@ -23268,21 +22858,6 @@ jest-message-util@^25.5.0: slash "^3.0.0" stack-utils "^1.0.1" -jest-message-util@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d" - integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^28.1.3" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^28.1.3" - slash "^3.0.0" - stack-utils "^2.0.3" - jest-message-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" @@ -23320,14 +22895,6 @@ jest-mock@^27.0.6: "@jest/types" "^27.5.1" "@types/node" "*" -jest-mock@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-28.1.3.tgz#d4e9b1fc838bea595c77ab73672ebf513ab249da" - integrity sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA== - dependencies: - "@jest/types" "^28.1.3" - "@types/node" "*" - jest-mock@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" @@ -23352,11 +22919,6 @@ jest-regex-util@^26.0.0: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== -jest-regex-util@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead" - integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw== - jest-regex-util@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" @@ -23371,14 +22933,6 @@ jest-resolve-dependencies@^25.5.4: jest-regex-util "^25.2.6" jest-snapshot "^25.5.1" -jest-resolve-dependencies@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz#8c65d7583460df7275c6ea2791901fa975c1fe66" - integrity sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA== - dependencies: - jest-regex-util "^28.0.2" - jest-snapshot "^28.1.3" - jest-resolve-dependencies@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" @@ -23402,21 +22956,6 @@ jest-resolve@^25.5.1: resolve "^1.17.0" slash "^3.0.0" -jest-resolve@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-28.1.3.tgz#cfb36100341ddbb061ec781426b3c31eb51aa0a8" - integrity sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ== - dependencies: - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-pnp-resolver "^1.2.2" - jest-util "^28.1.3" - jest-validate "^28.1.3" - resolve "^1.20.0" - resolve.exports "^1.1.0" - slash "^3.0.0" - jest-resolve@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" @@ -23457,33 +22996,6 @@ jest-runner@^25.5.4: source-map-support "^0.5.6" throat "^5.0.0" -jest-runner@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-28.1.3.tgz#5eee25febd730b4713a2cdfd76bdd5557840f9a1" - integrity sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA== - dependencies: - "@jest/console" "^28.1.3" - "@jest/environment" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.10.2" - graceful-fs "^4.2.9" - jest-docblock "^28.1.1" - jest-environment-node "^28.1.3" - jest-haste-map "^28.1.3" - jest-leak-detector "^28.1.3" - jest-message-util "^28.1.3" - jest-resolve "^28.1.3" - jest-runtime "^28.1.3" - jest-util "^28.1.3" - jest-watcher "^28.1.3" - jest-worker "^28.1.3" - p-limit "^3.1.0" - source-map-support "0.5.13" - jest-runner@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" @@ -23543,34 +23055,6 @@ jest-runtime@^25.5.4: strip-bom "^4.0.0" yargs "^15.3.1" -jest-runtime@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-28.1.3.tgz#a57643458235aa53e8ec7821949e728960d0605f" - integrity sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw== - dependencies: - "@jest/environment" "^28.1.3" - "@jest/fake-timers" "^28.1.3" - "@jest/globals" "^28.1.3" - "@jest/source-map" "^28.1.2" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - execa "^5.0.0" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-message-util "^28.1.3" - jest-mock "^28.1.3" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" - slash "^3.0.0" - strip-bom "^4.0.0" - jest-runtime@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" @@ -23635,35 +23119,6 @@ jest-snapshot@^25.5.1: pretty-format "^25.5.0" semver "^6.3.0" -jest-snapshot@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-28.1.3.tgz#17467b3ab8ddb81e2f605db05583d69388fc0668" - integrity sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg== - dependencies: - "@babel/core" "^7.11.6" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" - "@babel/types" "^7.3.3" - "@jest/expect-utils" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/babel__traverse" "^7.0.6" - "@types/prettier" "^2.1.5" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^28.1.3" - graceful-fs "^4.2.9" - jest-diff "^28.1.3" - jest-get-type "^28.0.2" - jest-haste-map "^28.1.3" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - natural-compare "^1.4.0" - pretty-format "^28.1.3" - semver "^7.3.5" - jest-snapshot@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" @@ -23713,18 +23168,6 @@ jest-util@^26.6.2: is-ci "^2.0.0" micromatch "^4.0.2" -jest-util@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" - integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ== - dependencies: - "@jest/types" "^28.1.3" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - jest-util@^29.0.0, jest-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" @@ -23749,18 +23192,6 @@ jest-validate@^25.5.0: leven "^3.1.0" pretty-format "^25.5.0" -jest-validate@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-28.1.3.tgz#e322267fd5e7c64cea4629612c357bbda96229df" - integrity sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA== - dependencies: - "@jest/types" "^28.1.3" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^28.0.2" - leven "^3.1.0" - pretty-format "^28.1.3" - jest-validate@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" @@ -23798,20 +23229,6 @@ jest-watcher@^25.2.4, jest-watcher@^25.5.0: jest-util "^25.5.0" string-length "^3.1.0" -jest-watcher@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-28.1.3.tgz#c6023a59ba2255e3b4c57179fc94164b3e73abd4" - integrity sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g== - dependencies: - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - emittery "^0.10.2" - jest-util "^28.1.3" - string-length "^4.0.1" - jest-watcher@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" @@ -23860,15 +23277,6 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.3.tgz#7e3c4ce3fa23d1bb6accb169e7f396f98ed4bb98" - integrity sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^8.0.0" - jest-worker@^29.1.2, jest-worker@^29.4.3, jest-worker@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" @@ -23888,16 +23296,6 @@ jest@^25.3.0: import-local "^3.0.2" jest-cli "^25.5.4" -jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest/-/jest-28.1.3.tgz#e9c6a7eecdebe3548ca2b18894a50f45b36dfc6b" - integrity sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA== - dependencies: - "@jest/core" "^28.1.3" - "@jest/types" "^28.1.3" - import-local "^3.0.2" - jest-cli "^28.1.3" - jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" @@ -30094,16 +29492,6 @@ pretty-format@^27.0.2: ansi-styles "^5.0.0" react-is "^17.0.1" -pretty-format@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" - integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q== - dependencies: - "@jest/schemas" "^28.1.3" - ansi-regex "^5.0.1" - ansi-styles "^5.0.0" - react-is "^18.0.0" - pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" @@ -31897,11 +31285,6 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== -resolve.exports@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" - integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== - resolve.exports@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" @@ -38191,7 +37574,7 @@ write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write-file-atomic@^4.0.1, write-file-atomic@^4.0.2: +write-file-atomic@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== From 1eb6fa05a0ac6b6d483702c8bca6078c3e85c59f Mon Sep 17 00:00:00 2001 From: abtestingalpha Date: Mon, 23 Sep 2024 16:57:56 +0000 Subject: [PATCH 15/90] Publish - @synapsecns/synapse-interface@0.38.9 --- packages/synapse-interface/CHANGELOG.md | 11 +++++++++++ packages/synapse-interface/package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/synapse-interface/CHANGELOG.md b/packages/synapse-interface/CHANGELOG.md index 7e358764a8..d6f956f26b 100644 --- a/packages/synapse-interface/CHANGELOG.md +++ b/packages/synapse-interface/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.38.9](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.38.8...@synapsecns/synapse-interface@0.38.9) (2024-09-23) + + +### Bug Fixes + +* **synapse-interface:** Additional checks on screen [SLT-166] ([#3152](https://github.com/synapsecns/sanguine/issues/3152)) ([9418b40](https://github.com/synapsecns/sanguine/commit/9418b40aa25a441d6a4460695962f7fbf41c4221)) + + + + + ## [0.38.8](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.38.7...@synapsecns/synapse-interface@0.38.8) (2024-09-20) **Note:** Version bump only for package @synapsecns/synapse-interface diff --git a/packages/synapse-interface/package.json b/packages/synapse-interface/package.json index 73c7d036b0..f271054021 100644 --- a/packages/synapse-interface/package.json +++ b/packages/synapse-interface/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/synapse-interface", - "version": "0.38.8", + "version": "0.38.9", "private": true, "engines": { "node": ">=18.18.0" From 6f21b1a7f6eb2ea3885582fcd678fa122f9f87e5 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Mon, 23 Sep 2024 14:16:38 -0700 Subject: [PATCH 16/90] feat(synapse-interface): confirm new price [SLT-150] (#3084) * add bridge quote history middleware * request user confirm changes when quoted price updates * add conditions for displaying confirm change state * track initial quote initializing confirm change state * specify output delta threshold * callback functions to handle initialize/accept/reset confirm changes flow * quote countdown timer animation to signal refresh * implement automatic refresh intervals * mouse move to refresh automatic intervals * add i8n translations for button text --------- Co-authored-by: abtestingalpha --- .../BridgeQuoteResetTimer.tsx | 113 ++++++++++++++++ .../BridgeTransactionButton.tsx | 37 +++++- .../StateManagedBridge/OutputContainer.tsx | 9 +- .../hooks/useBridgeValidations.ts | 2 +- .../hooks/useConfirmNewBridgePrice.ts | 125 ++++++++++++++++++ .../hooks/useStaleQuoteUpdater.ts | 114 ++++++++++++++++ .../components/buttons/TransactionButton.tsx | 3 +- .../components/ui/AmountInput.tsx | 3 + packages/synapse-interface/messages/ar.json | 3 +- .../synapse-interface/messages/en-US.json | 3 +- packages/synapse-interface/messages/es.json | 3 +- packages/synapse-interface/messages/fr.json | 3 +- packages/synapse-interface/messages/jp.json | 3 +- packages/synapse-interface/messages/tr.json | 3 +- .../synapse-interface/messages/zh-CN.json | 3 +- .../pages/state-managed-bridge/index.tsx | 55 +++++--- .../slices/bridgeQuote/reducer.ts | 8 +- .../bridgeQuoteHistoryMiddleware.ts | 25 ++++ .../destinationAddressMiddleware.ts | 0 packages/synapse-interface/store/store.ts | 9 +- .../utils/hooks/useStaleQuoteUpdater.ts | 50 ------- packages/synapse-interface/utils/time.ts | 4 + 22 files changed, 495 insertions(+), 83 deletions(-) create mode 100644 packages/synapse-interface/components/StateManagedBridge/BridgeQuoteResetTimer.tsx create mode 100644 packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts create mode 100644 packages/synapse-interface/components/StateManagedBridge/hooks/useStaleQuoteUpdater.ts create mode 100644 packages/synapse-interface/store/middleware/bridgeQuoteHistoryMiddleware.ts rename packages/synapse-interface/store/{ => middleware}/destinationAddressMiddleware.ts (100%) delete mode 100644 packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeQuoteResetTimer.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeQuoteResetTimer.tsx new file mode 100644 index 0000000000..86717650ba --- /dev/null +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeQuoteResetTimer.tsx @@ -0,0 +1,113 @@ +import { useState, useEffect, useMemo } from 'react' + +import { BridgeQuote } from '@/utils/types' +import { convertMsToSeconds } from '@/utils/time' + +export const BridgeQuoteResetTimer = ({ + bridgeQuote, + isLoading, + isActive, + duration, // in ms +}: { + bridgeQuote: BridgeQuote + isLoading: boolean + isActive: boolean + duration: number +}) => { + const memoizedTimer = useMemo(() => { + if (!isActive) return null + + if (isLoading) { + return + } else { + return ( + + ) + } + }, [bridgeQuote, duration, isActive]) + + return memoizedTimer +} + +const AnimatedLoadingCircle = () => { + return ( + + + + + + ) +} + +const AnimatedProgressCircle = ({ + animateKey, + duration, +}: { + animateKey: string + duration: number +}) => { + const [animationKey, setAnimationKey] = useState(0) + + useEffect(() => { + setAnimationKey((prevKey) => prevKey + 1) + }, [animateKey]) + + return ( + + + + + + + + + + + + ) +} diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index 44aa9a3fb5..86f6006650 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -12,6 +12,7 @@ import { useBridgeDisplayState, useBridgeState } from '@/slices/bridge/hooks' import { TransactionButton } from '@/components/buttons/TransactionButton' import { useBridgeValidations } from './hooks/useBridgeValidations' import { segmentAnalyticsEvent } from '@/contexts/SegmentAnalyticsProvider' +import { useConfirmNewBridgePrice } from './hooks/useConfirmNewBridgePrice' export const BridgeTransactionButton = ({ approveTxn, @@ -19,6 +20,7 @@ export const BridgeTransactionButton = ({ isApproved, isBridgePaused, isTyping, + isQuoteStale, }) => { const dispatch = useAppDispatch() const { openConnectModal } = useConnectModal() @@ -48,6 +50,8 @@ export const BridgeTransactionButton = ({ debouncedFromValue, } = useBridgeState() const { bridgeQuote, isLoading } = useBridgeQuoteState() + const { isPendingConfirmChange, onUserAcceptChange } = + useConfirmNewBridgePrice() const { isWalletPending } = useWalletState() const { showDestinationWarning, isDestinationWarningAccepted } = @@ -73,6 +77,7 @@ export const BridgeTransactionButton = ({ isBridgeQuoteAmountGreaterThanInputForRfq || (isConnected && !hasValidQuote) || (isConnected && !hasSufficientBalance) || + (isConnected && isQuoteStale) || (destinationAddress && !isAddress(destinationAddress)) let buttonProperties @@ -97,6 +102,26 @@ export const BridgeTransactionButton = ({ label: t('Please select an Origin token'), onClick: null, } + } else if (isConnected && !hasSufficientBalance) { + buttonProperties = { + label: t('Insufficient balance'), + onClick: null, + } + } else if (isLoading && hasValidQuote) { + buttonProperties = { + label: isPendingConfirmChange + ? t('Confirm new quote') + : t('Bridge {symbol}', { symbol: fromToken?.symbol }), + pendingLabel: t('Bridge {symbol}', { symbol: fromToken?.symbol }), + onClick: null, + className: ` + ${ + isPendingConfirmChange + ? '!outline !outline-1 !outline-synapsePurple !outline-offset-[-1px] !from-bgLight !to-bgLight' + : '!bg-gradient-to-r !from-fuchsia-500 !to-purple-500 dark:!to-purple-600' + } + !opacity-100`, + } } else if (isLoading) { buttonProperties = { label: t('Bridge {symbol}', { symbol: fromToken?.symbol }), @@ -144,11 +169,6 @@ export const BridgeTransactionButton = ({ label: t('Invalid bridge quote'), onClick: null, } - } else if (!isLoading && isConnected && !hasSufficientBalance) { - buttonProperties = { - label: t('Insufficient balance'), - onClick: null, - } } else if (destinationAddress && !isAddress(destinationAddress)) { buttonProperties = { label: t('Invalid Destination address'), @@ -167,6 +187,13 @@ export const BridgeTransactionButton = ({ onClick: () => switchChain({ chainId: fromChainId }), pendingLabel: t('Switching chains'), } + } else if (isApproved && hasValidQuote && isPendingConfirmChange) { + buttonProperties = { + label: t('Confirm new quote'), + onClick: () => onUserAcceptChange(), + className: + '!outline !outline-1 !outline-synapsePurple !outline-offset-[-1px] !from-bgLight !to-bgLight', + } } else if (!isApproved && hasValidInput && hasValidQuote) { buttonProperties = { onClick: approveTxn, diff --git a/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx b/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx index c891391fcd..5af733cb91 100644 --- a/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx @@ -17,7 +17,11 @@ import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks' import { useBridgeValidations } from './hooks/useBridgeValidations' import { useTranslations } from 'next-intl' -export const OutputContainer = () => { +interface OutputContainerProps { + isQuoteStale: boolean +} + +export const OutputContainer = ({ isQuoteStale }: OutputContainerProps) => { const { address } = useAccount() const { bridgeQuote, isLoading } = useBridgeQuoteState() const { showDestinationAddress } = useBridgeDisplayState() @@ -33,6 +37,8 @@ export const OutputContainer = () => { } }, [bridgeQuote, hasValidInput, hasValidQuote]) + const inputClassName = isQuoteStale ? 'opacity-50' : undefined + return (
@@ -48,6 +54,7 @@ export const OutputContainer = () => { disabled={true} showValue={showValue} isLoading={isLoading} + className={inputClassName} /> diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useBridgeValidations.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useBridgeValidations.ts index e64ac72587..b3f31ab0f6 100644 --- a/packages/synapse-interface/components/StateManagedBridge/hooks/useBridgeValidations.ts +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useBridgeValidations.ts @@ -111,7 +111,7 @@ export const useBridgeValidations = () => { } } -const constructStringifiedBridgeSelections = ( +export const constructStringifiedBridgeSelections = ( originAmount, originChainId, originToken, diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts new file mode 100644 index 0000000000..2dbead7f24 --- /dev/null +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts @@ -0,0 +1,125 @@ +import { useState, useEffect, useMemo, useRef } from 'react' + +import { useBridgeState } from '@/slices/bridge/hooks' +import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks' +import { constructStringifiedBridgeSelections } from './useBridgeValidations' +import { BridgeQuote } from '@/utils/types' + +export const useConfirmNewBridgePrice = () => { + const triggerQuoteRef = useRef(null) + const bpsThreshold = 0.0001 // 1bps + + const [hasQuoteOutputChanged, setHasQuoteOutputChanged] = + useState(false) + const [hasUserConfirmedChange, setHasUserConfirmedChange] = + useState(false) + + const { bridgeQuote, previousBridgeQuote } = useBridgeQuoteState() + const { debouncedFromValue, fromToken, toToken, fromChainId, toChainId } = + useBridgeState() + + const currentBridgeQuoteSelections = useMemo( + () => + constructStringifiedBridgeSelections( + debouncedFromValue, + fromChainId, + fromToken, + toChainId, + toToken + ), + [debouncedFromValue, fromChainId, fromToken, toChainId, toToken] + ) + + const previousBridgeQuoteSelections = useMemo( + () => + constructStringifiedBridgeSelections( + previousBridgeQuote?.inputAmountForQuote, + previousBridgeQuote?.originChainId, + previousBridgeQuote?.originTokenForQuote, + previousBridgeQuote?.destChainId, + previousBridgeQuote?.destTokenForQuote + ), + [previousBridgeQuote] + ) + + const hasSameSelectionsAsPreviousQuote = useMemo( + () => currentBridgeQuoteSelections === previousBridgeQuoteSelections, + [currentBridgeQuoteSelections, previousBridgeQuoteSelections] + ) + + const isPendingConfirmChange = + hasQuoteOutputChanged && + hasSameSelectionsAsPreviousQuote && + !hasUserConfirmedChange + + useEffect(() => { + const validQuotes = + bridgeQuote?.outputAmount && previousBridgeQuote?.outputAmount + + const hasBridgeModuleChanged = + bridgeQuote?.bridgeModuleName !== + (triggerQuoteRef.current?.bridgeModuleName ?? + previousBridgeQuote?.bridgeModuleName) + + const outputAmountDiffMoreThanThreshold = validQuotes + ? calculateOutputRelativeDifference( + bridgeQuote, + triggerQuoteRef.current ?? previousBridgeQuote + ) > bpsThreshold + : false + + if ( + validQuotes && + hasSameSelectionsAsPreviousQuote && + (outputAmountDiffMoreThanThreshold || hasBridgeModuleChanged) + ) { + requestUserConfirmChange(previousBridgeQuote) + } else { + resetConfirm() + } + }, [bridgeQuote, previousBridgeQuote, hasSameSelectionsAsPreviousQuote]) + + const requestUserConfirmChange = (previousQuote: BridgeQuote) => { + if (!hasQuoteOutputChanged && !hasUserConfirmedChange) { + triggerQuoteRef.current = previousQuote + setHasQuoteOutputChanged(true) + } + setHasUserConfirmedChange(false) + } + + const resetConfirm = () => { + if (hasUserConfirmedChange) { + triggerQuoteRef.current = null + setHasQuoteOutputChanged(false) + setHasUserConfirmedChange(false) + } + } + + const onUserAcceptChange = () => { + triggerQuoteRef.current = null + setHasUserConfirmedChange(true) + } + + return { + isPendingConfirmChange, + onUserAcceptChange, + } +} + +const calculateOutputRelativeDifference = ( + currentQuote?: BridgeQuote, + previousQuote?: BridgeQuote +) => { + if (!currentQuote?.outputAmountString || !previousQuote?.outputAmountString) { + return null + } + + const currentOutput = parseFloat(currentQuote.outputAmountString) + const previousOutput = parseFloat(previousQuote.outputAmountString) + + if (previousOutput === 0) { + return null + } + + return (previousOutput - currentOutput) / previousOutput +} diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useStaleQuoteUpdater.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useStaleQuoteUpdater.ts new file mode 100644 index 0000000000..9a9cbf1444 --- /dev/null +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useStaleQuoteUpdater.ts @@ -0,0 +1,114 @@ +import { useEffect, useRef, useState } from 'react' + +import { BridgeQuote } from '@/utils/types' +import { useIntervalTimer } from '@/utils/hooks/useIntervalTimer' + +export const useStaleQuoteUpdater = ( + quote: BridgeQuote, + refreshQuoteCallback: () => Promise, + enabled: boolean, + staleTimeout: number = 15000, // in ms + autoRefreshDuration: number = 30000 // in ms +) => { + const [isStale, setIsStale] = useState(false) + const autoRefreshIntervalRef = useRef(null) + const autoRefreshStartTimeRef = useRef(null) + const mouseMoveListenerRef = useRef void)>(null) + const manualRefreshRef = useRef(null) + + useIntervalTimer(staleTimeout, !enabled) + + const [mouseMoved, resetMouseMove] = useTrackMouseMove() + + const clearManualRefreshTimeout = () => { + if (manualRefreshRef.current) { + clearTimeout(manualRefreshRef.current) + } + } + + const clearAutoRefreshInterval = () => { + if (autoRefreshIntervalRef.current) { + clearInterval(autoRefreshIntervalRef.current) + } + } + + const clearMouseMoveListener = () => { + if (mouseMoveListenerRef.current) { + mouseMoveListenerRef.current = null + } + } + + useEffect(() => { + if (mouseMoved && autoRefreshStartTimeRef.current) { + autoRefreshStartTimeRef.current = null + resetMouseMove() + } + }, [quote]) + + // Start auto-refresh logic for ${autoRefreshDuration}ms seconds + useEffect(() => { + if (enabled) { + // If auto-refresh has not started yet, initialize the start time + if (autoRefreshStartTimeRef.current === null) { + autoRefreshStartTimeRef.current = Date.now() + } + + const elapsedTime = Date.now() - autoRefreshStartTimeRef.current + + // If ${autoRefreshDuration}ms hasn't passed, keep auto-refreshing + if (elapsedTime < autoRefreshDuration) { + clearManualRefreshTimeout() + clearAutoRefreshInterval() + + autoRefreshIntervalRef.current = setInterval(() => { + refreshQuoteCallback() + }, staleTimeout) + } else { + // If more than ${autoRefreshDuration}ms have passed, stop auto-refreshing and switch to mousemove logic + clearAutoRefreshInterval() + + manualRefreshRef.current = setTimeout(() => { + clearMouseMoveListener() + setIsStale(true) + + const handleMouseMove = () => { + refreshQuoteCallback() + clearMouseMoveListener() + setIsStale(false) + } + + document.addEventListener('mousemove', handleMouseMove, { + once: true, + }) + + mouseMoveListenerRef.current = handleMouseMove + }, staleTimeout) + } + } + + return () => { + clearManualRefreshTimeout() + clearAutoRefreshInterval() + setIsStale(false) + } + }, [quote, enabled]) + + return isStale +} + +export const useTrackMouseMove = (): [boolean, () => void] => { + const [moved, setMoved] = useState(false) + + const onMove = () => setMoved(true) + const onReset = () => setMoved(false) + + useEffect(() => { + document.addEventListener('mousemove', onMove) + + return () => { + document.removeEventListener('mousemove', onMove) + } + }, []) + + return [moved, onReset] +} diff --git a/packages/synapse-interface/components/buttons/TransactionButton.tsx b/packages/synapse-interface/components/buttons/TransactionButton.tsx index e868868dc8..24461d6526 100644 --- a/packages/synapse-interface/components/buttons/TransactionButton.tsx +++ b/packages/synapse-interface/components/buttons/TransactionButton.tsx @@ -12,6 +12,7 @@ const baseClassNames = { disabled: 'disabled:opacity-50 disabled:cursor-not-allowed', background: 'bg-zinc-400 dark:bg-bgLight', gradient: 'enabled:bg-gradient-to-r', + transition: 'transition', } export const TransactionButton = ({ @@ -42,8 +43,8 @@ export const TransactionButton = ({ style={style} disabled={disabled} className={` - ${className} ${joinClassNames(baseClassNames)} + ${className} ${ isPending ? 'from-fuchsia-400 dark:from-fuchsia-900 to-purple-400 dark:to-purple-900' diff --git a/packages/synapse-interface/components/ui/AmountInput.tsx b/packages/synapse-interface/components/ui/AmountInput.tsx index ed352f5176..aebc3fd3a7 100644 --- a/packages/synapse-interface/components/ui/AmountInput.tsx +++ b/packages/synapse-interface/components/ui/AmountInput.tsx @@ -11,6 +11,7 @@ interface AmountInputTypes { showValue: string handleFromValueChange?: (event: React.ChangeEvent) => void setIsTyping?: (isTyping: boolean) => void + className?: string } export function AmountInput({ @@ -20,6 +21,7 @@ export function AmountInput({ showValue, handleFromValueChange, setIsTyping, + className, }: AmountInputTypes) { const debouncedSetIsTyping = useCallback( debounce((value: boolean) => setIsTyping?.(value), 600), @@ -38,6 +40,7 @@ export function AmountInput({ placeholder: 'placeholder:text-zinc-500 placeholder:dark:text-zinc-400', font: 'text-xl md:text-2xl font-medium', focus: 'focus:outline-none focus:ring-0 focus:border-none', + custom: className, } return ( diff --git a/packages/synapse-interface/messages/ar.json b/packages/synapse-interface/messages/ar.json index b52b6f617d..fc6571c49e 100644 --- a/packages/synapse-interface/messages/ar.json +++ b/packages/synapse-interface/messages/ar.json @@ -57,7 +57,8 @@ "ReceiveWithEllipsis": "استلام…", "All receivable tokens": "جميع الرموز القابلة للاستلام", "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "تم العثور على مسار لجسر {debouncedFromValue} {fromToken} على {fromChainId} إلى {toToken} على {toChainId}", - "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "لم يتم العثور على مسار لجسر {debouncedFromValue} {fromToken} على {fromChainId} إلى {toToken} على {toChainId}" + "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "لم يتم العثور على مسار لجسر {debouncedFromValue} {fromToken} على {fromChainId} إلى {toToken} على {toChainId}", + "Confirm new quote": "تأكيد العرض الجديد" }, "Completed": { "to": "إلى", diff --git a/packages/synapse-interface/messages/en-US.json b/packages/synapse-interface/messages/en-US.json index 4784ee82d4..7c5a99c5d0 100644 --- a/packages/synapse-interface/messages/en-US.json +++ b/packages/synapse-interface/messages/en-US.json @@ -57,7 +57,8 @@ "ReceiveWithEllipsis": "Receive…", "All receivable tokens": "All receivable tokens", "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}", - "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}" + "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}", + "Confirm new quote": "Confirm new quote" }, "Completed": { "to": "to", diff --git a/packages/synapse-interface/messages/es.json b/packages/synapse-interface/messages/es.json index dee3724b09..c6ac15e7d8 100644 --- a/packages/synapse-interface/messages/es.json +++ b/packages/synapse-interface/messages/es.json @@ -57,7 +57,8 @@ "ReceiveWithEllipsis": "Recibir…", "All receivable tokens": "Todos los tokens recibibles", "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "Ruta encontrada para el puente de {debouncedFromValue} {fromToken} en {fromChainId} a {toToken} en {toChainId}", - "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "No se encontró ruta para el puente de {debouncedFromValue} {fromToken} en {fromChainId} a {toToken} en {toChainId}" + "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "No se encontró ruta para el puente de {debouncedFromValue} {fromToken} en {fromChainId} a {toToken} en {toChainId}", + "Confirm new quote": "Confirmar nueva cotización" }, "Completed": { "to": "a", diff --git a/packages/synapse-interface/messages/fr.json b/packages/synapse-interface/messages/fr.json index 2541e4c68f..b5f249aaf1 100644 --- a/packages/synapse-interface/messages/fr.json +++ b/packages/synapse-interface/messages/fr.json @@ -57,7 +57,8 @@ "ReceiveWithEllipsis": "Recevoir…", "All receivable tokens": "Tous les jetons recevables", "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "Route trouvée pour le transfert de {debouncedFromValue} {fromToken} sur {fromChainId} vers {toToken} sur {toChainId}", - "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "Aucune route trouvée pour le transfert de {debouncedFromValue} {fromToken} sur {fromChainId} vers {toToken} sur {toChainId}" + "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "Aucune route trouvée pour le transfert de {debouncedFromValue} {fromToken} sur {fromChainId} vers {toToken} sur {toChainId}", + "Confirm new quote": "Confirmer la nouvelle offre" }, "Completed": { "to": "vers", diff --git a/packages/synapse-interface/messages/jp.json b/packages/synapse-interface/messages/jp.json index 1d23dd39a9..c187de083b 100644 --- a/packages/synapse-interface/messages/jp.json +++ b/packages/synapse-interface/messages/jp.json @@ -57,7 +57,8 @@ "ReceiveWithEllipsis": "受取…", "All receivable tokens": "すべての受取可能トークン", "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "{fromChainId}の{debouncedFromValue} {fromToken}から{toChainId}の{toToken}へのブリッジのルートが見つかりました", - "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "{fromChainId}の{debouncedFromValue} {fromToken}から{toChainId}の{toToken}へのブリッジのルートが見つかりませんでした" + "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "{fromChainId}の{debouncedFromValue} {fromToken}から{toChainId}の{toToken}へのブリッジのルートが見つかりませんでした", + "Confirm new quote": "新しい見積もりを確認" }, "Completed": { "to": "へ", diff --git a/packages/synapse-interface/messages/tr.json b/packages/synapse-interface/messages/tr.json index 34ad00a1f9..dbe44d71b2 100644 --- a/packages/synapse-interface/messages/tr.json +++ b/packages/synapse-interface/messages/tr.json @@ -57,7 +57,8 @@ "ReceiveWithEllipsis": "Al…", "All receivable tokens": "Tüm alınabilir tokenler", "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "{fromChainId}'deki {debouncedFromValue} {fromToken}'ı {toChainId}'deki {toToken}'a köprüleme için rota bulundu", - "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "{fromChainId}'deki {debouncedFromValue} {fromToken}'ı {toChainId}'deki {toToken}'a köprüleme için rota bulunamadı" + "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "{fromChainId}'deki {debouncedFromValue} {fromToken}'ı {toChainId}'deki {toToken}'a köprüleme için rota bulunamadı", + "Confirm new quote": "Yeni teklifi onayla" }, "Completed": { "to": "nereye", diff --git a/packages/synapse-interface/messages/zh-CN.json b/packages/synapse-interface/messages/zh-CN.json index 34a2b7500d..0d5cb9e02d 100644 --- a/packages/synapse-interface/messages/zh-CN.json +++ b/packages/synapse-interface/messages/zh-CN.json @@ -57,7 +57,8 @@ "ReceiveWithEllipsis": "接收…", "All receivable tokens": "所有可接收代币", "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "找到桥接路线:从 {fromChainId} 的 {fromToken} 到 {toChainId} 的 {toToken}", - "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "未找到桥接路线:从 {fromChainId} 的 {fromToken} 到 {toChainId} 的 {toToken}" + "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "未找到桥接路线:从 {fromChainId} 的 {fromToken} 到 {toChainId} 的 {toToken}", + "Confirm new quote": "确认新报价" }, "Completed": { "to": "到", diff --git a/packages/synapse-interface/pages/state-managed-bridge/index.tsx b/packages/synapse-interface/pages/state-managed-bridge/index.tsx index 31a04528f0..e3128e0d6d 100644 --- a/packages/synapse-interface/pages/state-managed-bridge/index.tsx +++ b/packages/synapse-interface/pages/state-managed-bridge/index.tsx @@ -58,7 +58,6 @@ import { RootState } from '@/store/store' import { getUnixTimeMinutesFromNow } from '@/utils/time' import { isTransactionReceiptError } from '@/utils/isTransactionReceiptError' import { wagmiConfig } from '@/wagmiConfig' -import { useStaleQuoteUpdater } from '@/utils/hooks/useStaleQuoteUpdater' import { useMaintenance } from '@/components/Maintenance/Maintenance' import { screenAddress } from '@/utils/screenAddress' import { useWalletState } from '@/slices/wallet/hooks' @@ -66,10 +65,14 @@ import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks' import { resetBridgeQuote } from '@/slices/bridgeQuote/reducer' import { fetchBridgeQuote } from '@/slices/bridgeQuote/thunks' import { useIsBridgeApproved } from '@/utils/hooks/useIsBridgeApproved' +import { isTransactionUserRejectedError } from '@/utils/isTransactionUserRejectedError' +import { BridgeQuoteResetTimer } from '@/components/StateManagedBridge/BridgeQuoteResetTimer' +import { useBridgeValidations } from '@/components/StateManagedBridge/hooks/useBridgeValidations' +import { useStaleQuoteUpdater } from '@/components/StateManagedBridge/hooks/useStaleQuoteUpdater' const StateManagedBridge = () => { const dispatch = useAppDispatch() - const { address } = useAccount() + const { address, isConnected } = useAccount() const { synapseSDK } = useSynapseContext() const router = useRouter() const { query, pathname } = router @@ -96,6 +99,8 @@ const StateManagedBridge = () => { const isApproved = useIsBridgeApproved() + const { hasValidQuote, hasSufficientBalance } = useBridgeValidations() + const { isWalletPending } = useWalletState() const { showSettingsSlideOver } = useSelector( @@ -141,8 +146,6 @@ const StateManagedBridge = () => { // will have to handle deadlineMinutes here at later time, gets passed as optional last arg in .bridgeQuote() - /* clear stored bridge quote before requesting new bridge quote */ - dispatch(resetBridgeQuote()) const currentTimestamp: number = getUnixTimeMinutesFromNow(0) try { @@ -221,11 +224,18 @@ const StateManagedBridge = () => { } } - useStaleQuoteUpdater( + const isUpdaterEnabled = + isConnected && + hasValidQuote && + hasSufficientBalance && + isApproved && + !isBridgePaused && + !isWalletPending + + const isQuoteStale = useStaleQuoteUpdater( bridgeQuote, getAndSetBridgeQuote, - isLoading, - isWalletPending, + isUpdaterEnabled, quoteTimeout ) @@ -428,6 +438,10 @@ const StateManagedBridge = () => { ) } + if (isTransactionUserRejectedError(error)) { + getAndSetBridgeQuote() + } + return txErrorHandler(error) } finally { dispatch(setIsWalletPending(false)) @@ -471,18 +485,29 @@ const StateManagedBridge = () => { }} disabled={isWalletPending} /> - + - +
+ +
+ +
+
)} diff --git a/packages/synapse-interface/slices/bridgeQuote/reducer.ts b/packages/synapse-interface/slices/bridgeQuote/reducer.ts index 8407876035..898769f622 100644 --- a/packages/synapse-interface/slices/bridgeQuote/reducer.ts +++ b/packages/synapse-interface/slices/bridgeQuote/reducer.ts @@ -6,11 +6,13 @@ import { fetchBridgeQuote } from './thunks' export interface BridgeQuoteState { bridgeQuote: BridgeQuote + previousBridgeQuote: BridgeQuote | null isLoading: boolean } export const initialState: BridgeQuoteState = { bridgeQuote: EMPTY_BRIDGE_QUOTE, + previousBridgeQuote: null, isLoading: false, } @@ -24,6 +26,9 @@ export const bridgeQuoteSlice = createSlice({ resetBridgeQuote: (state) => { state.bridgeQuote = initialState.bridgeQuote }, + setPreviousBridgeQuote: (state, action: PayloadAction) => { + state.previousBridgeQuote = action.payload + }, }, extraReducers: (builder) => { builder @@ -44,6 +49,7 @@ export const bridgeQuoteSlice = createSlice({ }, }) -export const { resetBridgeQuote, setIsLoading } = bridgeQuoteSlice.actions +export const { resetBridgeQuote, setIsLoading, setPreviousBridgeQuote } = + bridgeQuoteSlice.actions export default bridgeQuoteSlice.reducer diff --git a/packages/synapse-interface/store/middleware/bridgeQuoteHistoryMiddleware.ts b/packages/synapse-interface/store/middleware/bridgeQuoteHistoryMiddleware.ts new file mode 100644 index 0000000000..f58c09ae03 --- /dev/null +++ b/packages/synapse-interface/store/middleware/bridgeQuoteHistoryMiddleware.ts @@ -0,0 +1,25 @@ +import { + Middleware, + MiddlewareAPI, + Dispatch, + AnyAction, +} from '@reduxjs/toolkit' + +export const bridgeQuoteHistoryMiddleware: Middleware = + (store: MiddlewareAPI) => (next: Dispatch) => (action: AnyAction) => { + const previousState = store.getState() + const result = next(action) + const currentState = store.getState() + + if ( + previousState.bridgeQuote.bridgeQuote !== + currentState.bridgeQuote.bridgeQuote + ) { + store.dispatch({ + type: 'bridgeQuote/setPreviousBridgeQuote', + payload: previousState.bridgeQuote.bridgeQuote, + }) + } + + return result + } diff --git a/packages/synapse-interface/store/destinationAddressMiddleware.ts b/packages/synapse-interface/store/middleware/destinationAddressMiddleware.ts similarity index 100% rename from packages/synapse-interface/store/destinationAddressMiddleware.ts rename to packages/synapse-interface/store/middleware/destinationAddressMiddleware.ts diff --git a/packages/synapse-interface/store/store.ts b/packages/synapse-interface/store/store.ts index 70f5515a6f..d8cdf3e70a 100644 --- a/packages/synapse-interface/store/store.ts +++ b/packages/synapse-interface/store/store.ts @@ -6,7 +6,8 @@ import { api } from '@/slices/api/slice' import { segmentAnalyticsEvent } from '@/contexts/SegmentAnalyticsProvider' import { storageKey, persistConfig, persistedReducer } from './reducer' import { resetReduxCache } from '@/slices/application/actions' -import { destinationAddressMiddleware } from '@/store/destinationAddressMiddleware' +import { destinationAddressMiddleware } from '@/store/middleware/destinationAddressMiddleware' +import { bridgeQuoteHistoryMiddleware } from './middleware/bridgeQuoteHistoryMiddleware' const checkVersionAndResetCache = (): boolean => { if (typeof window !== 'undefined') { @@ -28,7 +29,11 @@ export const store = configureStore({ middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false, - }).concat(api.middleware, destinationAddressMiddleware), + }).concat( + api.middleware, + destinationAddressMiddleware, + bridgeQuoteHistoryMiddleware + ), }) if (checkVersionAndResetCache()) { diff --git a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts deleted file mode 100644 index 05ccd37dc3..0000000000 --- a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { isNull, isNumber } from 'lodash' -import { useEffect, useRef } from 'react' - -import { BridgeQuote } from '@/utils/types' -import { calculateTimeBetween } from '@/utils/time' -import { useIntervalTimer } from '@/utils/hooks/useIntervalTimer' -import { convertUuidToUnix } from '@/utils/convertUuidToUnix' - -/** - * Refreshes quotes based on selected stale timeout duration. - * Will refresh quote when browser is active and wallet prompt is not pending. - */ -export const useStaleQuoteUpdater = ( - quote: BridgeQuote, - refreshQuoteCallback: () => Promise, - isQuoteLoading: boolean, - isWalletPending: boolean, - staleTimeout: number = 15000 // Default 15_000ms or 15s -) => { - const eventListenerRef = useRef void)>(null) - - const quoteTime = quote?.id ? convertUuidToUnix(quote?.id) : null - const isValidQuote = isNumber(quoteTime) && !isNull(quoteTime) - - const currentTime = useIntervalTimer(staleTimeout, !isValidQuote) - - useEffect(() => { - if (isValidQuote && !isQuoteLoading && !isWalletPending) { - const timeDifference = calculateTimeBetween(currentTime, quoteTime) - const isStaleQuote = timeDifference >= staleTimeout - - if (isStaleQuote) { - if (eventListenerRef.current) { - document.removeEventListener('mousemove', eventListenerRef.current) - } - - const newEventListener = () => { - refreshQuoteCallback() - eventListenerRef.current = null - } - - document.addEventListener('mousemove', newEventListener, { - once: true, - }) - - eventListenerRef.current = newEventListener - } - } - }, [currentTime, staleTimeout]) -} diff --git a/packages/synapse-interface/utils/time.ts b/packages/synapse-interface/utils/time.ts index 8e30b5075a..20a8c042f0 100644 --- a/packages/synapse-interface/utils/time.ts +++ b/packages/synapse-interface/utils/time.ts @@ -51,3 +51,7 @@ export const isTimestampToday = (unixTimestamp: number): boolean => { dateFromTimestamp.getFullYear() === currentDate.getFullYear() ) } + +export const convertMsToSeconds = (ms: number) => { + return Math.ceil(ms / 1000) +} From 1de17e0cdf7d90de8920e4844720de57fabf59ec Mon Sep 17 00:00:00 2001 From: bigboydiamonds Date: Mon, 23 Sep 2024 21:21:01 +0000 Subject: [PATCH 17/90] Publish - @synapsecns/synapse-interface@0.39.0 --- packages/synapse-interface/CHANGELOG.md | 11 +++++++++++ packages/synapse-interface/package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/synapse-interface/CHANGELOG.md b/packages/synapse-interface/CHANGELOG.md index d6f956f26b..bdaf008aa9 100644 --- a/packages/synapse-interface/CHANGELOG.md +++ b/packages/synapse-interface/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.39.0](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.38.9...@synapsecns/synapse-interface@0.39.0) (2024-09-23) + + +### Features + +* **synapse-interface:** confirm new price [SLT-150] ([#3084](https://github.com/synapsecns/sanguine/issues/3084)) ([6f21b1a](https://github.com/synapsecns/sanguine/commit/6f21b1a7f6eb2ea3885582fcd678fa122f9f87e5)) + + + + + ## [0.38.9](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.38.8...@synapsecns/synapse-interface@0.38.9) (2024-09-23) diff --git a/packages/synapse-interface/package.json b/packages/synapse-interface/package.json index f271054021..97217d87a4 100644 --- a/packages/synapse-interface/package.json +++ b/packages/synapse-interface/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/synapse-interface", - "version": "0.38.9", + "version": "0.39.0", "private": true, "engines": { "node": ">=18.18.0" From b6651b1a19fbb6341ddf4bb3303d7aa9fa6f616e Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Mon, 23 Sep 2024 15:18:39 -0700 Subject: [PATCH 18/90] fix: formatted bridge fee amount (#3165) --- .../src/controllers/bridgeController.ts | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/packages/rest-api/src/controllers/bridgeController.ts b/packages/rest-api/src/controllers/bridgeController.ts index 7bb703631b..568c4eb0fc 100644 --- a/packages/rest-api/src/controllers/bridgeController.ts +++ b/packages/rest-api/src/controllers/bridgeController.ts @@ -25,17 +25,24 @@ export const bridgeController = async (req, res) => { toToken, amountInWei ) - const payload = resp.map((quote) => ({ - ...quote, - maxAmountOutStr: formatBNToString( - quote.maxAmountOut, - toTokenInfo.decimals - ), - bridgeFeeFormatted: formatBNToString( - quote.feeAmount, - toTokenInfo.decimals - ), - })) + + const payload = resp.map((quote) => { + const originQueryTokenOutInfo = tokenAddressToToken( + fromChain.toString(), + quote.originQuery.tokenOut + ) + return { + ...quote, + maxAmountOutStr: formatBNToString( + quote.maxAmountOut, + toTokenInfo.decimals + ), + bridgeFeeFormatted: formatBNToString( + quote.feeAmount, + originQueryTokenOutInfo.decimals + ), + } + }) res.json(payload) } catch (err) { res.status(500).json({ From 0daec701eb7afafa5ae53d8792c058f29f500d01 Mon Sep 17 00:00:00 2001 From: bigboydiamonds Date: Mon, 23 Sep 2024 22:23:08 +0000 Subject: [PATCH 19/90] Publish - @synapsecns/rest-api@1.1.5 --- packages/rest-api/CHANGELOG.md | 11 +++++++++++ packages/rest-api/package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/rest-api/CHANGELOG.md b/packages/rest-api/CHANGELOG.md index b4e0738437..286079ef3d 100644 --- a/packages/rest-api/CHANGELOG.md +++ b/packages/rest-api/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.1.5](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.1.4...@synapsecns/rest-api@1.1.5) (2024-09-23) + + +### Bug Fixes + +* formatted bridge fee amount ([#3165](https://github.com/synapsecns/sanguine/issues/3165)) ([b6651b1](https://github.com/synapsecns/sanguine/commit/b6651b1a19fbb6341ddf4bb3303d7aa9fa6f616e)) + + + + + ## [1.1.4](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.1.3...@synapsecns/rest-api@1.1.4) (2024-09-21) **Note:** Version bump only for package @synapsecns/rest-api diff --git a/packages/rest-api/package.json b/packages/rest-api/package.json index a6f4d23916..4d0f56ef06 100644 --- a/packages/rest-api/package.json +++ b/packages/rest-api/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/rest-api", - "version": "1.1.4", + "version": "1.1.5", "private": "true", "engines": { "node": ">=18.17.0" From 74b620e4c928be8d0dbb422708376d167db7848d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CF=87=C2=B2?= <88190723+ChiTimesChi@users.noreply.github.com> Date: Tue, 24 Sep 2024 19:49:32 +0200 Subject: [PATCH 20/90] fix(contracts-rfq): CI workflows [SLT-245] (#3178) * fix: license, files * fix: package name * build: update solhint to latest * build: remove prettier dependencies * fix: solhint workflows * build: update solhint in other packages as well * chore: solhint rules, exceptions * fix: silence linter warnings in tests * chore: forge fmt * add variable to test linter CI * Revert "add variable to test linter CI" This reverts commit 0629309a4077063aacb464c4bbafbe7f37ff38c6. --- packages/contracts-core/package.json | 2 +- packages/contracts-rfq/.solhint.json | 13 +- packages/contracts-rfq/.solhintignore | 5 + .../contracts/interfaces/IFastBridgeV2.sol | 2 - packages/contracts-rfq/package.json | 24 +- .../contracts-rfq/test/FastBridgeV2.t.sol | 2 +- packages/contracts-rfq/test/MockERC20.sol | 8 +- .../test/UniversalTokenLib.t.sol | 1 + .../test/UniversalTokenLibHarness.sol | 1 + packages/solidity-devops/package.json | 2 +- yarn.lock | 248 ++---------------- 11 files changed, 60 insertions(+), 248 deletions(-) create mode 100644 packages/contracts-rfq/.solhintignore diff --git a/packages/contracts-core/package.json b/packages/contracts-core/package.json index 48dd32ba5a..a46aef09be 100644 --- a/packages/contracts-core/package.json +++ b/packages/contracts-core/package.json @@ -28,7 +28,7 @@ "@typechain/ethers-v5": "10.0.0", "hardhat": "2.22.2", "rimraf": "3.0.2", - "solhint": "3.3.8", + "solhint": "5.0.3", "typechain": "8.0.0", "typescript": "4.7.3" }, diff --git a/packages/contracts-rfq/.solhint.json b/packages/contracts-rfq/.solhint.json index ce2220e0b7..2a0106eecd 100644 --- a/packages/contracts-rfq/.solhint.json +++ b/packages/contracts-rfq/.solhint.json @@ -1,3 +1,14 @@ { - "extends": "solhint:recommended" + "extends": "solhint:recommended", + "rules": { + "code-complexity": ["error"], + "func-name-mixedcase": "error", + "func-visibility": ["error", { "ignoreConstructors": true }], + "gas-custom-errors": "off", + "immutable-vars-naming": ["error", { "immutablesAsConstants": false }], + "max-line-length": ["warn", 120], + "modifier-name-mixedcase": "error", + "ordering": "warn", + "var-name-mixedcase": "error" + } } diff --git a/packages/contracts-rfq/.solhintignore b/packages/contracts-rfq/.solhintignore new file mode 100644 index 0000000000..b1133d75d8 --- /dev/null +++ b/packages/contracts-rfq/.solhintignore @@ -0,0 +1,5 @@ +contracts/FastBridge.sol +contracts/interfaces/IFastBridge.sol +script/FastBridge.s.sol +test/FastBridge.t.sol +test/FastBridgeMock.sol diff --git a/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol index 979551919a..2002a4ba14 100644 --- a/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol +++ b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.20; import {IFastBridge} from "./IFastBridge.sol"; interface IFastBridgeV2 is IFastBridge { - /// @notice Relays destination side of bridge transaction by off-chain relayer /// @param request The encoded bridge transaction to relay on destination chain /// @param relayer The address of the relaying entity which should have control of the origin funds when claimed @@ -19,5 +18,4 @@ interface IFastBridgeV2 is IFastBridge { /// @notice Completes bridge transaction on origin chain by claiming originally deposited capital. Can only send funds to the relayer address on the proof. /// @param request The encoded bridge transaction to claim on origin chain function claim(bytes memory request) external; - } diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index 44666d9fc7..fff3c066ec 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,20 +1,18 @@ { - "name": "FastBridge", - "license": "UNLICENSED", + "name": "@synapsecns/contracts-rfq", + "license": "MIT", "version": "0.4.1", "description": "FastBridge contracts.", "private": true, "files": [ - "src/*.sol" + "contracts/**/*.sol" ], "dependencies": { "@openzeppelin/contracts": "5.0.1" }, "devDependencies": { "@synapsecns/solidity-devops": "^0.4.5", - "prettier": "^2.5.1", - "prettier-plugin-solidity": "^1.0.0-beta.19", - "solhint": "^3.3.6" + "solhint": "5.0.3" }, "scripts": { "build": " ", @@ -22,15 +20,11 @@ "build:slither": "forge build --out=out --build-info --force", "test:coverage": "echo 'Please use foundry'", "test": "forge test", - "lint:contracts:fix": "forge fmt && solhint --fix -c .solhint.json '{contracts,script,test}/**/*.sol'", - "lint": "forge fmt && npm run prettier && npm run solhint", - "ci:lint": "yarn lint", + "lint": "forge fmt && npm run solhint", + "lint:check": "forge fmt --check && npm run solhint:check", + "ci:lint": "npm run lint:check", "build:go": "./flatten.sh contracts/*.sol test/*.sol", - "prettier": "prettier --write **.sol", - "prettier:list": "prettier --list-different **.sol", - "prettier:check": "prettier --check **.sol", - "solhint": "solhint --config ./.solhint.json 'src/**/*.sol' --fix", - "solhint:check": "solhint --config ./.solhint.json 'src/**/*.sol'", - "lint:check": "npm run prettier:check && npm run solhint:check" + "solhint": "solhint '{contracts,script,test}/**/*.sol' --fix --noPrompt", + "solhint:check": "solhint '{contracts,script,test}/**/*.sol'" } } diff --git a/packages/contracts-rfq/test/FastBridgeV2.t.sol b/packages/contracts-rfq/test/FastBridgeV2.t.sol index 4fe357e3f2..39881c5151 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.t.sol @@ -10,7 +10,7 @@ import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol" import {Test} from "forge-std/Test.sol"; import {stdStorage, StdStorage} from "forge-std/Test.sol"; -// solhint-disable no-empty-blocks +// solhint-disable no-empty-blocks, ordering abstract contract FastBridgeV2Test is Test { using stdStorage for StdStorage; diff --git a/packages/contracts-rfq/test/MockERC20.sol b/packages/contracts-rfq/test/MockERC20.sol index 9736a896c7..f4c07d49aa 100644 --- a/packages/contracts-rfq/test/MockERC20.sol +++ b/packages/contracts-rfq/test/MockERC20.sol @@ -10,10 +10,6 @@ contract MockERC20 is ERC20 { _decimals = decimals_; } - function decimals() public view override returns (uint8) { - return _decimals; - } - function burn(address account, uint256 amount) external { _burn(account, amount); } @@ -21,4 +17,8 @@ contract MockERC20 is ERC20 { function mint(address account, uint256 amount) external { _mint(account, amount); } + + function decimals() public view override returns (uint8) { + return _decimals; + } } diff --git a/packages/contracts-rfq/test/UniversalTokenLib.t.sol b/packages/contracts-rfq/test/UniversalTokenLib.t.sol index 19e796e52d..58bbd34435 100644 --- a/packages/contracts-rfq/test/UniversalTokenLib.t.sol +++ b/packages/contracts-rfq/test/UniversalTokenLib.t.sol @@ -8,6 +8,7 @@ import {MockRevertingRecipient} from "./MockRevertingRecipient.sol"; import {Test} from "forge-std/Test.sol"; +// solhint-disable ordering contract UniversalTokenLibraryTest is Test { UniversalTokenLibHarness public libHarness; MockERC20 public token; diff --git a/packages/contracts-rfq/test/UniversalTokenLibHarness.sol b/packages/contracts-rfq/test/UniversalTokenLibHarness.sol index b4ea21182b..7f8d2d6753 100644 --- a/packages/contracts-rfq/test/UniversalTokenLibHarness.sol +++ b/packages/contracts-rfq/test/UniversalTokenLibHarness.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.17; import {UniversalTokenLib} from "../contracts/libs/UniversalToken.sol"; +// solhint-disable ordering contract UniversalTokenLibHarness { function universalTransfer(address token, address to, uint256 value) public { UniversalTokenLib.universalTransfer(token, to, value); diff --git a/packages/solidity-devops/package.json b/packages/solidity-devops/package.json index 3819d952b1..c8794f7918 100644 --- a/packages/solidity-devops/package.json +++ b/packages/solidity-devops/package.json @@ -40,6 +40,6 @@ "vp": "js/verifyProxy.js" }, "devDependencies": { - "solhint": "^4.5.4" + "solhint": "5.0.3" } } diff --git a/yarn.lock b/yarn.lock index 39e01c8b37..826b7331b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7383,20 +7383,13 @@ resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2" integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== -"@solidity-parser/parser@^0.14.0", "@solidity-parser/parser@^0.14.5": +"@solidity-parser/parser@^0.14.0": version "0.14.5" resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.5.tgz#87bc3cc7b068e08195c219c91cd8ddff5ef1a804" integrity sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg== dependencies: antlr4ts "^0.5.0-alpha.4" -"@solidity-parser/parser@^0.16.0": - version "0.16.2" - resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.16.2.tgz#42cb1e3d88b3e8029b0c9befff00b634cd92d2fa" - integrity sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg== - dependencies: - antlr4ts "^0.5.0-alpha.4" - "@solidity-parser/parser@^0.18.0": version "0.18.0" resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.18.0.tgz#8e77a02a09ecce957255a2f48c9a7178ec191908" @@ -11231,7 +11224,7 @@ acorn-walk@^8.0.0, acorn-walk@^8.1.1, acorn-walk@^8.2.0: dependencies: acorn "^8.11.0" -acorn@^6.0.1, acorn@^6.0.7, acorn@^6.4.1: +acorn@^6.0.1, acorn@^6.4.1: version "6.4.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== @@ -11382,7 +11375,7 @@ ajv@8.6.3: require-from-string "^2.0.2" uri-js "^4.2.2" -ajv@^6.0.0, ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.12.6, ajv@^6.6.1, ajv@^6.9.1: +ajv@^6.0.0, ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.12.6: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -11464,7 +11457,7 @@ ansi-colors@^4.1.1, ansi-colors@^4.1.3: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== -ansi-escapes@^3.0.0, ansi-escapes@^3.2.0: +ansi-escapes@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== @@ -11557,12 +11550,7 @@ ansi-to-html@^0.6.11: dependencies: entities "^2.0.0" -antlr4@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.7.1.tgz#69984014f096e9e775f53dd9744bf994d8959773" - integrity sha512-haHyTW7Y9joE5MVs37P2lNYfU2RWBLfcRDD8OWldcdZm5TiCE91B5Xl1oWSwiDUSd4rlExpt2pu1fksYQjRBYQ== - -antlr4@^4.11.0, antlr4@^4.13.1-patch-1: +antlr4@^4.13.1-patch-1: version "4.13.2" resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.13.2.tgz#0d084ad0e32620482a9c3a0e2470c02e72e4006d" integrity sha512-QiVbZhyy4xAZ17UPEuG3YTOt8ZaoeOR1CvEAqrEsDBsOqINslaB147i9xqljZqoyf5S+EUlGStaj+t22LT9MOg== @@ -11964,7 +11952,7 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== -ast-parents@0.0.1, ast-parents@^0.0.1: +ast-parents@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/ast-parents/-/ast-parents-0.0.1.tgz#508fd0f05d0c48775d9eccda2e174423261e8dd3" integrity sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA== @@ -13288,30 +13276,11 @@ call-me-maybe@^1.0.1: resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.2.tgz#03f964f19522ba643b1b0693acb9152fe2074baa" integrity sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ== -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" - integrity sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ== - dependencies: - callsites "^2.0.0" - -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" - integrity sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A== - dependencies: - caller-callsite "^2.0.0" - callsite@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" integrity sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ== -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" - integrity sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ== - callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -13839,7 +13808,7 @@ cli-boxes@^3.0.0: resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145" integrity sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g== -cli-cursor@^2.0.0, cli-cursor@^2.1.0: +cli-cursor@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" integrity sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw== @@ -13895,11 +13864,6 @@ cli-truncate@^3.1.0: slice-ansi "^5.0.0" string-width "^5.0.0" -cli-width@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" - integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== - cli-width@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" @@ -14180,11 +14144,6 @@ command-line-usage@^6.1.0: table-layout "^1.0.2" typical "^5.2.0" -commander@2.18.0: - version "2.18.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" - integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ== - commander@2.20.3, commander@^2.19.0, commander@^2.20.0, commander@^2.8.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -14697,16 +14656,6 @@ cors@^2.8.1: object-assign "^4" vary "^1" -cosmiconfig@^5.0.7: - version "5.2.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - cosmiconfig@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" @@ -17552,7 +17501,7 @@ eslint-scope@^7.2.2: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-utils@^1.3.1, eslint-utils@^1.4.3: +eslint-utils@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== @@ -17588,48 +17537,6 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@^5.6.0: - version "5.16.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" - integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== - dependencies: - "@babel/code-frame" "^7.0.0" - ajv "^6.9.1" - chalk "^2.1.0" - cross-spawn "^6.0.5" - debug "^4.0.1" - doctrine "^3.0.0" - eslint-scope "^4.0.3" - eslint-utils "^1.3.1" - eslint-visitor-keys "^1.0.0" - espree "^5.0.1" - esquery "^1.0.1" - esutils "^2.0.2" - file-entry-cache "^5.0.1" - functional-red-black-tree "^1.0.1" - glob "^7.1.2" - globals "^11.7.0" - ignore "^4.0.6" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - inquirer "^6.2.2" - js-yaml "^3.13.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.11" - minimatch "^3.0.4" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.2" - progress "^2.0.0" - regexpp "^2.0.1" - semver "^5.5.1" - strip-ansi "^4.0.0" - strip-json-comments "^2.0.1" - table "^5.2.3" - text-table "^0.2.0" - eslint@^6.1.0: version "6.8.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" @@ -17727,15 +17634,6 @@ esniff@^2.0.1: event-emitter "^0.3.5" type "^2.7.2" -espree@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" - integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== - dependencies: - acorn "^6.0.7" - acorn-jsx "^5.0.0" - eslint-visitor-keys "^1.0.0" - espree@^6.1.2: version "6.2.1" resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" @@ -18701,13 +18599,6 @@ figures@^1.3.5: escape-string-regexp "^1.0.5" object-assign "^4.1.0" -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" - integrity sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA== - dependencies: - escape-string-regexp "^1.0.5" - figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -19791,7 +19682,7 @@ global@^4.4.0, global@~4.4.0: min-document "^2.19.0" process "^0.11.10" -globals@^11.1.0, globals@^11.7.0: +globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== @@ -21254,14 +21145,6 @@ import-cwd@^3.0.0: dependencies: import-from "^3.0.0" -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" - integrity sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg== - dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" - import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -21419,25 +21302,6 @@ inline-style-parser@0.2.4: resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.4.tgz#f4af5fe72e612839fcd453d989a586566d695f22" integrity sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q== -inquirer@^6.2.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" - integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== - dependencies: - ansi-escapes "^3.2.0" - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^3.0.3" - figures "^2.0.0" - lodash "^4.17.12" - mute-stream "0.0.7" - run-async "^2.2.0" - rxjs "^6.4.0" - string-width "^2.1.0" - strip-ansi "^5.1.0" - through "^2.3.6" - inquirer@^7.0.0, inquirer@^7.3.3: version "7.3.3" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" @@ -21777,11 +21641,6 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-accessor-descriptor "^1.0.1" is-data-descriptor "^1.0.1" -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" - integrity sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw== - is-docker@^2.0.0, is-docker@^2.1.1: version "2.2.1" resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" @@ -23371,7 +23230,7 @@ js-string-escape@^1.0.1: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@3.14.1, js-yaml@3.x, js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.14.1, js-yaml@^3.6.1: +js-yaml@3.14.1, js-yaml@3.x, js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.14.1, js-yaml@^3.6.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -24163,7 +24022,7 @@ lodash.uniq@4.5.0, lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== -lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0, lodash@~4.17.0: +lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0, lodash@~4.17.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -26428,11 +26287,6 @@ mustache@^4.2.0: resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== -mute-stream@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" - integrity sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ== - mute-stream@0.0.8, mute-stream@~0.0.4: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" @@ -27512,7 +27366,7 @@ optimism@^0.18.0: "@wry/trie" "^0.4.3" tslib "^2.3.0" -optionator@^0.8.1, optionator@^0.8.2, optionator@^0.8.3: +optionator@^0.8.1, optionator@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== @@ -28190,7 +28044,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-is-inside@1.0.2, path-is-inside@^1.0.2: +path-is-inside@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== @@ -29424,7 +29278,7 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier-plugin-solidity@^1.0.0-beta.13, prettier-plugin-solidity@^1.0.0-beta.19: +prettier-plugin-solidity@^1.0.0-beta.13: version "1.4.1" resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.4.1.tgz#8060baf18853a9e34d2e09e47e87b4f19e15afe9" integrity sha512-Mq8EtfacVZ/0+uDKTtHZGW3Aa7vEbX/BNx63hmVg6YTiTXSiuKP0amj0G6pGwjmLaOfymWh3QgXEZkjQbU8QRg== @@ -29437,12 +29291,12 @@ prettier-plugin-solidity@^1.0.0-beta.13, prettier-plugin-solidity@^1.0.0-beta.19 resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18" integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w== -prettier@^1.14.3, prettier@^1.19.1: +prettier@^1.19.1: version "1.19.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== -prettier@^2.3.1, prettier@^2.5.1, prettier@^2.7.1, prettier@^2.8.0, prettier@^2.8.3: +prettier@^2.3.1, prettier@^2.7.1, prettier@^2.8.0, prettier@^2.8.3: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== @@ -31260,11 +31114,6 @@ resolve-from@5.0.0, resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" - integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== - resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -31591,7 +31440,7 @@ rtlcss@^4.1.0: postcss "^8.4.21" strip-json-comments "^3.1.1" -run-async@^2.2.0, run-async@^2.4.0: +run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== @@ -31610,7 +31459,7 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.4.0, rxjs@^6.6.0: +rxjs@^6.6.0: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== @@ -31906,7 +31755,7 @@ semver-truncate@^1.1.2: dependencies: semver "^5.3.0" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== @@ -32558,57 +32407,10 @@ solhint-plugin-prettier@^0.0.5: dependencies: prettier-linter-helpers "^1.0.0" -solhint@3.3.8: - version "3.3.8" - resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.3.8.tgz#b1773c881cfaf0b5008c78ad658a69603d3fa051" - integrity sha512-TkYyJ6uUJCaiqRKuhHhFuoAoyco9Ia+RDKhl3usjG/rkaNk8/LdLRla2Xln7MVdBTaPKNAU8ezTRSit50Yy4qw== - dependencies: - "@solidity-parser/parser" "^0.14.5" - ajv "^6.6.1" - antlr4 "4.7.1" - ast-parents "0.0.1" - chalk "^2.4.2" - commander "2.18.0" - cosmiconfig "^5.0.7" - eslint "^5.6.0" - fast-diff "^1.1.2" - glob "^7.1.3" - ignore "^4.0.6" - js-yaml "^3.12.0" - lodash "^4.17.11" - semver "^6.3.0" - optionalDependencies: - prettier "^1.14.3" - -solhint@^3.3.6: - version "3.6.2" - resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.6.2.tgz#2b2acbec8fdc37b2c68206a71ba89c7f519943fe" - integrity sha512-85EeLbmkcPwD+3JR7aEMKsVC9YrRSxd4qkXuMzrlf7+z2Eqdfm1wHWq1ffTuo5aDhoZxp2I9yF3QkxZOxOL7aQ== - dependencies: - "@solidity-parser/parser" "^0.16.0" - ajv "^6.12.6" - antlr4 "^4.11.0" - ast-parents "^0.0.1" - chalk "^4.1.2" - commander "^10.0.0" - cosmiconfig "^8.0.0" - fast-diff "^1.2.0" - glob "^8.0.3" - ignore "^5.2.4" - js-yaml "^4.1.0" - lodash "^4.17.21" - pluralize "^8.0.0" - semver "^7.5.2" - strip-ansi "^6.0.1" - table "^6.8.1" - text-table "^0.2.0" - optionalDependencies: - prettier "^2.8.3" - -solhint@^4.5.4: - version "4.5.4" - resolved "https://registry.yarnpkg.com/solhint/-/solhint-4.5.4.tgz#171cf33f46c36b8499efe60c0e425f6883a54e50" - integrity sha512-Cu1XiJXub2q1eCr9kkJ9VPv1sGcmj3V7Zb76B0CoezDOB9bu3DxKIFFH7ggCl9fWpEPD6xBmRLfZrYijkVmujQ== +solhint@5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/solhint/-/solhint-5.0.3.tgz#b57f6d2534fe09a60f9db1b92e834363edd1cbde" + integrity sha512-OLCH6qm/mZTCpplTXzXTJGId1zrtNuDYP5c2e6snIv/hdRVxPfBBz/bAlL91bY/Accavkayp2Zp2BaDSrLVXTQ== dependencies: "@solidity-parser/parser" "^0.18.0" ajv "^6.12.6" @@ -33230,7 +33032,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.1.0, string-width@^2.1.1: +"string-width@^1.0.2 || 2", string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -33484,7 +33286,7 @@ strip-json-comments@4.0.0: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-4.0.0.tgz#6fd3a79f1b956905483769b0bf66598b8f87da50" integrity sha512-LzWcbfMbAsEDTRmhjWIioe8GcDRl0fa35YMXFoJKDdiD/quGFmjJjdgPjFJJNwCMaLyQqFIDqCdHD2V4HfLgYA== -strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: +strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== From 038605dbfb5acbab7f1970988e7df67a2446eaa3 Mon Sep 17 00:00:00 2001 From: ChiTimesChi Date: Tue, 24 Sep 2024 17:53:42 +0000 Subject: [PATCH 21/90] Publish - @synapsecns/contracts-core@1.0.33 - @synapsecns/contracts-rfq@0.5.0 - @synapsecns/solidity-devops@0.4.6 --- packages/contracts-core/CHANGELOG.md | 11 +++++++++++ packages/contracts-core/package.json | 2 +- packages/contracts-rfq/CHANGELOG.md | 25 +++++++++++++++++++++++++ packages/contracts-rfq/package.json | 4 ++-- packages/solidity-devops/CHANGELOG.md | 11 +++++++++++ packages/solidity-devops/package.json | 2 +- 6 files changed, 51 insertions(+), 4 deletions(-) diff --git a/packages/contracts-core/CHANGELOG.md b/packages/contracts-core/CHANGELOG.md index 83c627a3fe..266f1aacf3 100644 --- a/packages/contracts-core/CHANGELOG.md +++ b/packages/contracts-core/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.0.33](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-core@1.0.32...@synapsecns/contracts-core@1.0.33) (2024-09-24) + + +### Bug Fixes + +* **contracts-rfq:** CI workflows [SLT-245] ([#3178](https://github.com/synapsecns/sanguine/issues/3178)) ([74b620e](https://github.com/synapsecns/sanguine/commit/74b620e4c928be8d0dbb422708376d167db7848d)) + + + + + ## [1.0.32](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-core@1.0.31...@synapsecns/contracts-core@1.0.32) (2024-09-23) **Note:** Version bump only for package @synapsecns/contracts-core diff --git a/packages/contracts-core/package.json b/packages/contracts-core/package.json index a46aef09be..7413177aae 100644 --- a/packages/contracts-core/package.json +++ b/packages/contracts-core/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/contracts-core", - "version": "1.0.32", + "version": "1.0.33", "description": "", "scripts": { "build": "yarn build:contracts && yarn build:typescript && yarn build:go", diff --git a/packages/contracts-rfq/CHANGELOG.md b/packages/contracts-rfq/CHANGELOG.md index daa73da3e7..e70355a860 100644 --- a/packages/contracts-rfq/CHANGELOG.md +++ b/packages/contracts-rfq/CHANGELOG.md @@ -3,6 +3,31 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# 0.5.0 (2024-09-24) + + +### Bug Fixes + +* adjust to [#2658](https://github.com/synapsecns/sanguine/issues/2658) breaking changes ([#2688](https://github.com/synapsecns/sanguine/issues/2688)) ([7533f70](https://github.com/synapsecns/sanguine/commit/7533f70f4bc88f334af26a009c81c9d581d3997f)) +* **contracts-rfq:** CI workflows [SLT-245] ([#3178](https://github.com/synapsecns/sanguine/issues/3178)) ([74b620e](https://github.com/synapsecns/sanguine/commit/74b620e4c928be8d0dbb422708376d167db7848d)) + + +### Features + +* **contracts-rfq:** Deploy new RFQ Contracts ([#2255](https://github.com/synapsecns/sanguine/issues/2255)) ([b3a51e2](https://github.com/synapsecns/sanguine/commit/b3a51e28c037a93fb62fd064c3c2df5e901bd79d)) +* **contracts-rfq:** Multicall target abstraction [SLT-134] ([#3078](https://github.com/synapsecns/sanguine/issues/3078)) ([100324f](https://github.com/synapsecns/sanguine/commit/100324f269f77f73fc10913d0162676f5f918996)) +* **contracts-rfq:** relay/prove/claim with different address [SLT-130] ([#3138](https://github.com/synapsecns/sanguine/issues/3138)) ([23f6c4c](https://github.com/synapsecns/sanguine/commit/23f6c4c652743c5ca7a184ad730ce19af3600a9c)) +* **rfq:** Deploy RFQ to Base ([#2397](https://github.com/synapsecns/sanguine/issues/2397)) ([b73538e](https://github.com/synapsecns/sanguine/commit/b73538e93dfe6ecc0ce6806e75d4d1c59328c7b3)) + + +### Reverts + +* Revert "update bl" ([3693110](https://github.com/synapsecns/sanguine/commit/3693110e0f9df6177935bbbfba5444df62b11866)) + + + + + ## [0.4.1](https://github.com/synapsecns/sanguine/compare/FastBridge@0.4.0...FastBridge@0.4.1) (2024-09-23) **Note:** Version bump only for package FastBridge diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index fff3c066ec..d868a0738e 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/contracts-rfq", "license": "MIT", - "version": "0.4.1", + "version": "0.5.0", "description": "FastBridge contracts.", "private": true, "files": [ @@ -11,7 +11,7 @@ "@openzeppelin/contracts": "5.0.1" }, "devDependencies": { - "@synapsecns/solidity-devops": "^0.4.5", + "@synapsecns/solidity-devops": "^0.4.6", "solhint": "5.0.3" }, "scripts": { diff --git a/packages/solidity-devops/CHANGELOG.md b/packages/solidity-devops/CHANGELOG.md index b7149765ba..262bd0a971 100644 --- a/packages/solidity-devops/CHANGELOG.md +++ b/packages/solidity-devops/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.6](https://github.com/synapsecns/sanguine/compare/@synapsecns/solidity-devops@0.4.5...@synapsecns/solidity-devops@0.4.6) (2024-09-24) + + +### Bug Fixes + +* **contracts-rfq:** CI workflows [SLT-245] ([#3178](https://github.com/synapsecns/sanguine/issues/3178)) ([74b620e](https://github.com/synapsecns/sanguine/commit/74b620e4c928be8d0dbb422708376d167db7848d)) + + + + + ## [0.4.5](https://github.com/synapsecns/sanguine/compare/@synapsecns/solidity-devops@0.4.4...@synapsecns/solidity-devops@0.4.5) (2024-09-23) **Note:** Version bump only for package @synapsecns/solidity-devops diff --git a/packages/solidity-devops/package.json b/packages/solidity-devops/package.json index c8794f7918..b69ffa6e81 100644 --- a/packages/solidity-devops/package.json +++ b/packages/solidity-devops/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/solidity-devops", - "version": "0.4.5", + "version": "0.4.6", "description": "A collection of utils to effortlessly test, deploy and maintain the smart contracts on EVM compatible blockchains", "license": "MIT", "repository": { From 98362bb7cd8972d83d7a628b2db4fb06831871c0 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Tue, 24 Sep 2024 12:33:47 -0700 Subject: [PATCH 22/90] feat(api): bridge limits [SLT-165] (#3179) * adds `/bridgeLimits` route, controller * fetch best sdk quote for min/max origin amounts * add tests * implement middleware to normalize addresses * adds swagger doc --- packages/rest-api/package.json | 1 + .../src/controllers/bridgeLimitsController.ts | 108 ++++++++++++++ .../rest-api/src/routes/bridgeLimitsRoute.ts | 138 ++++++++++++++++++ packages/rest-api/src/routes/index.ts | 2 + .../src/tests/bridgeLimitsRoute.test.ts | 81 ++++++++++ 5 files changed, 330 insertions(+) create mode 100644 packages/rest-api/src/controllers/bridgeLimitsController.ts create mode 100644 packages/rest-api/src/routes/bridgeLimitsRoute.ts create mode 100644 packages/rest-api/src/tests/bridgeLimitsRoute.test.ts diff --git a/packages/rest-api/package.json b/packages/rest-api/package.json index 4d0f56ef06..79c289ccd0 100644 --- a/packages/rest-api/package.json +++ b/packages/rest-api/package.json @@ -18,6 +18,7 @@ "test:coverage": "jest --collect-coverage" }, "dependencies": { + "@ethersproject/address": "^5.7.0", "@ethersproject/bignumber": "^5.7.0", "@ethersproject/providers": "^5.7.2", "@ethersproject/units": "5.7.0", diff --git a/packages/rest-api/src/controllers/bridgeLimitsController.ts b/packages/rest-api/src/controllers/bridgeLimitsController.ts new file mode 100644 index 0000000000..1ef292338f --- /dev/null +++ b/packages/rest-api/src/controllers/bridgeLimitsController.ts @@ -0,0 +1,108 @@ +import { validationResult } from 'express-validator' +import { BigNumber } from 'ethers' +import { parseUnits } from '@ethersproject/units' + +import { Synapse } from '../services/synapseService' +import { tokenAddressToToken } from '../utils/tokenAddressToToken' +import { formatBNToString } from '../utils/formatBNToString' + +export const bridgeLimitsController = async (req, res) => { + const errors = validationResult(req) + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }) + } + try { + const { fromChain, fromToken, toChain, toToken } = req.query + + const fromTokenInfo = tokenAddressToToken(fromChain, fromToken) + const toTokenInfo = tokenAddressToToken(toChain, toToken) + + const upperLimitValue = parseUnits('1000000', fromTokenInfo.decimals) + const upperLimitBridgeQuotes = await Synapse.allBridgeQuotes( + Number(fromChain), + Number(toChain), + fromTokenInfo.address, + toTokenInfo.address, + upperLimitValue + ) + + const lowerLimitValues = ['0.01', '10'] + let lowerLimitBridgeQuotes = null + + for (const limit of lowerLimitValues) { + const lowerLimitAmount = parseUnits(limit, fromTokenInfo.decimals) + + lowerLimitBridgeQuotes = await Synapse.allBridgeQuotes( + Number(fromChain), + Number(toChain), + fromTokenInfo.address, + toTokenInfo.address, + lowerLimitAmount + ) + + if (lowerLimitBridgeQuotes && lowerLimitBridgeQuotes.length > 0) { + break + } + } + + const maxBridgeAmountQuote = upperLimitBridgeQuotes.reduce( + (maxQuote, currentQuote) => { + const currentMaxAmount = currentQuote.maxAmountOut + const maxAmount = maxQuote ? maxQuote.maxAmountOut : BigNumber.from(0) + + return currentMaxAmount.gt(maxAmount) ? currentQuote : maxQuote + }, + null + ) + + const minBridgeAmountQuote = lowerLimitBridgeQuotes.reduce( + (minQuote, currentQuote) => { + const currentFeeAmount = currentQuote.feeAmount + const minFeeAmount = minQuote ? minQuote.feeAmount : null + + return !minFeeAmount || currentFeeAmount.lt(minFeeAmount) + ? currentQuote + : minQuote + }, + null + ) + + if (!maxBridgeAmountQuote || !minBridgeAmountQuote) { + return res.json({ + maxOriginAmount: null, + minOriginAmount: null, + }) + } + + const maxAmountOriginQueryTokenOutInfo = tokenAddressToToken( + toChain, + maxBridgeAmountQuote.destQuery.tokenOut + ) + + const minAmountOriginQueryTokenOutInfo = tokenAddressToToken( + fromChain, + minBridgeAmountQuote.originQuery.tokenOut + ) + + const maxOriginAmount = formatBNToString( + maxBridgeAmountQuote.maxAmountOut, + maxAmountOriginQueryTokenOutInfo.decimals + ) + + const minOriginAmount = formatBNToString( + minBridgeAmountQuote.feeAmount, + minAmountOriginQueryTokenOutInfo.decimals + ) + + return res.json({ + maxOriginAmount, + minOriginAmount, + }) + } catch (err) { + res.status(500).json({ + error: + 'An unexpected error occurred in /bridgeLimits. Please try again later.', + details: err.message, + }) + } +} diff --git a/packages/rest-api/src/routes/bridgeLimitsRoute.ts b/packages/rest-api/src/routes/bridgeLimitsRoute.ts new file mode 100644 index 0000000000..14ab637671 --- /dev/null +++ b/packages/rest-api/src/routes/bridgeLimitsRoute.ts @@ -0,0 +1,138 @@ +import express from 'express' +import { check } from 'express-validator' + +import { CHAINS_ARRAY } from '../constants/chains' +import { showFirstValidationError } from '../middleware/showFirstValidationError' +import { bridgeLimitsController } from '../controllers/bridgeLimitsController' +import { isTokenSupportedOnChain } from './../utils/isTokenSupportedOnChain' +import { isTokenAddress } from '../utils/isTokenAddress' +import { normalizeNativeTokenAddress } from '../middleware/normalizeNativeTokenAddress' +import { checksumAddresses } from '../middleware/checksumAddresses' + +const router = express.Router() + +/** + * @openapi + * /bridgeLimits: + * get: + * summary: Get min/max origin values for bridge quote + * description: Retrieve min/max bridgeable amounts to bridge from source chain to destination chain. Returns null for min/max amounts if limits are unavailable. + * parameters: + * - in: query + * name: fromChain + * required: true + * schema: + * type: integer + * description: The source chain ID. + * - in: query + * name: toChain + * required: true + * schema: + * type: integer + * description: The destination chain ID. + * - in: query + * name: fromToken + * required: true + * schema: + * type: string + * description: The address of the token on the source chain. + * - in: query + * name: toToken + * required: true + * schema: + * type: string + * description: The address of the token on the destination chain. + * responses: + * 200: + * description: Successful response containing min and max origin amounts. + * content: + * application/json: + * schema: + * type: object + * properties: + * maxOriginAmount: + * type: string + * description: Maximum amount of tokens that can be bridged from the origin chain. + * minOriginAmount: + * type: string + * description: Minimum amount of tokens that can be bridged from the origin chain. + * example: + * maxOriginAmount: "999600" + * minOriginAmount: "4" + * 400: + * description: Invalid input + * content: + * application/json: + * schema: + * type: object + * properties: + * error: + * type: object + * properties: + * value: + * type: string + * message: + * type: string + * field: + * type: string + * location: + * type: string + * example: + * error: + * value: "999" + * message: "Unsupported fromChain" + * field: "fromChain" + * location: "query" + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * error: + * type: string + * details: + * type: string + */ +router.get( + '/', + normalizeNativeTokenAddress(['fromToken', 'toToken']), + checksumAddresses(['fromToken', 'toToken']), + [ + check('fromChain') + .exists() + .withMessage('fromChain is required') + .isNumeric() + .custom((value) => CHAINS_ARRAY.some((c) => c.id === Number(value))) + .withMessage('Unsupported fromChain'), + check('toChain') + .exists() + .withMessage('toChain is required') + .isNumeric() + .custom((value) => CHAINS_ARRAY.some((c) => c.id === Number(value))) + .withMessage('Unsupported toChain'), + check('fromToken') + .exists() + .withMessage('fromToken is required') + .custom((value) => isTokenAddress(value)) + .withMessage('Invalid fromToken address') + .custom((value, { req }) => + isTokenSupportedOnChain(value, req.query.fromChain as string) + ) + .withMessage('Token not supported on specified chain'), + check('toToken') + .exists() + .withMessage('toToken is required') + .custom((value) => isTokenAddress(value)) + .withMessage('Invalid toToken address') + .custom((value, { req }) => + isTokenSupportedOnChain(value, req.query.toChain as string) + ) + .withMessage('Token not supported on specified chain'), + ], + showFirstValidationError, + bridgeLimitsController +) + +export default router diff --git a/packages/rest-api/src/routes/index.ts b/packages/rest-api/src/routes/index.ts index 1bbcf3ea51..2c5e1c547e 100644 --- a/packages/rest-api/src/routes/index.ts +++ b/packages/rest-api/src/routes/index.ts @@ -10,6 +10,7 @@ import bridgeTxStatusRoute from './bridgeTxStatusRoute' import destinationTxRoute from './destinationTxRoute' import tokenListRoute from './tokenListRoute' import destinationTokensRoute from './destinationTokensRoute' +import bridgeLimitsRoute from './bridgeLimitsRoute' const router = express.Router() @@ -18,6 +19,7 @@ router.use('/swap', swapRoute) router.use('/swapTxInfo', swapTxInfoRoute) router.use('/bridge', bridgeRoute) router.use('/bridgeTxInfo', bridgeTxInfoRoute) +router.use('/bridgeLimits', bridgeLimitsRoute) router.use('/synapseTxId', synapseTxIdRoute) router.use('/bridgeTxStatus', bridgeTxStatusRoute) router.use('/destinationTx', destinationTxRoute) diff --git a/packages/rest-api/src/tests/bridgeLimitsRoute.test.ts b/packages/rest-api/src/tests/bridgeLimitsRoute.test.ts new file mode 100644 index 0000000000..921393773b --- /dev/null +++ b/packages/rest-api/src/tests/bridgeLimitsRoute.test.ts @@ -0,0 +1,81 @@ +import request from 'supertest' +import express from 'express' + +import bridgeLimitsRoute from '../routes/bridgeLimitsRoute' +import { USDC, ETH } from '../constants/bridgeable' + +const app = express() +app.use('/bridgeLimits', bridgeLimitsRoute) + +describe('Get Bridge Limits Route', () => { + it('should return min/max origin amounts bridging USDC', async () => { + const response = await request(app).get('/bridgeLimits').query({ + fromChain: 1, + fromToken: USDC.addresses[1], + toChain: 10, + toToken: USDC.addresses[10], + }) + + expect(response.status).toBe(200) + expect(response.body).toHaveProperty('maxOriginAmount') + expect(response.body).toHaveProperty('minOriginAmount') + }, 10_000) + + it('should return min/max origin amounts bridging ETH', async () => { + const response = await request(app).get('/bridgeLimits').query({ + fromChain: 1, + fromToken: ETH.addresses[1], + toChain: 10, + toToken: ETH.addresses[10], + }) + + expect(response.status).toBe(200) + expect(response.body).toHaveProperty('maxOriginAmount') + expect(response.body).toHaveProperty('minOriginAmount') + }, 10_000) + + it('should return 400 for unsupported fromChain', async () => { + const response = await request(app).get('/bridgeLimits').query({ + fromChain: '999', + toChain: '137', + fromToken: '0x176211869cA2b568f2A7D4EE941E073a821EE1ff', + toToken: USDC.addresses[137], + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty( + 'message', + 'Unsupported fromChain' + ) + }, 10_000) + + it('should return 400 for unsupported toChain', async () => { + const response = await request(app).get('/bridgeLimits').query({ + fromChain: '137', + toChain: '999', + fromToken: USDC.addresses[137], + toToken: '0x176211869cA2b568f2A7D4EE941E073a821EE1ff', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('message', 'Unsupported toChain') + }, 10_000) + + it('should return 400 for missing fromToken', async () => { + const response = await request(app).get('/bridgeLimits').query({ + fromChain: '1', + toChain: '137', + toToken: USDC.addresses[137], + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'fromToken') + }, 10_000) + + it('should return 400 for missing toToken', async () => { + const response = await request(app).get('/bridgeLimits').query({ + fromChain: '1', + toChain: '137', + fromToken: USDC.addresses[1], + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'toToken') + }, 10_000) +}) From 67a04cd3f00c3f45bf5fcdc4b84e36d20f1aff4b Mon Sep 17 00:00:00 2001 From: bigboydiamonds Date: Tue, 24 Sep 2024 19:38:04 +0000 Subject: [PATCH 23/90] Publish - @synapsecns/rest-api@1.2.0 --- packages/rest-api/CHANGELOG.md | 11 +++++++++++ packages/rest-api/package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/rest-api/CHANGELOG.md b/packages/rest-api/CHANGELOG.md index 286079ef3d..ac53ec49bf 100644 --- a/packages/rest-api/CHANGELOG.md +++ b/packages/rest-api/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.2.0](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.1.5...@synapsecns/rest-api@1.2.0) (2024-09-24) + + +### Features + +* **api:** bridge limits [SLT-165] ([#3179](https://github.com/synapsecns/sanguine/issues/3179)) ([98362bb](https://github.com/synapsecns/sanguine/commit/98362bb7cd8972d83d7a628b2db4fb06831871c0)) + + + + + ## [1.1.5](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.1.4...@synapsecns/rest-api@1.1.5) (2024-09-23) diff --git a/packages/rest-api/package.json b/packages/rest-api/package.json index 79c289ccd0..e6836b39f0 100644 --- a/packages/rest-api/package.json +++ b/packages/rest-api/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/rest-api", - "version": "1.1.5", + "version": "1.2.0", "private": "true", "engines": { "node": ">=18.17.0" From c15ec8691902817f096589553bac360b53ba40cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CF=87=C2=B2?= <88190723+ChiTimesChi@users.noreply.github.com> Date: Wed, 25 Sep 2024 13:29:04 +0200 Subject: [PATCH 24/90] fix(contracts-rfq): limit the amount of solhint warnings [SLT-245] (#3182) * ci: limit the amount of solhint warnings * refactor: move the errors into the separate interface * refactor: errors imports in tests --- .../contracts-rfq/contracts/FastBridgeV2.sol | 4 ++-- .../interfaces/IFastBridgeV2Errors.sol | 19 +++++++++++++++++++ packages/contracts-rfq/package.json | 4 ++-- .../contracts-rfq/test/FastBridgeV2.Dst.t.sol | 2 -- .../test/FastBridgeV2.Parity.t.sol | 6 ++++-- .../contracts-rfq/test/FastBridgeV2.Src.t.sol | 13 ------------- .../contracts-rfq/test/FastBridgeV2.t.sol | 3 ++- 7 files changed, 29 insertions(+), 22 deletions(-) create mode 100644 packages/contracts-rfq/contracts/interfaces/IFastBridgeV2Errors.sol diff --git a/packages/contracts-rfq/contracts/FastBridgeV2.sol b/packages/contracts-rfq/contracts/FastBridgeV2.sol index 7f938f28f1..50acfca74c 100644 --- a/packages/contracts-rfq/contracts/FastBridgeV2.sol +++ b/packages/contracts-rfq/contracts/FastBridgeV2.sol @@ -3,14 +3,14 @@ pragma solidity 0.8.24; import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import "./libs/Errors.sol"; import {UniversalTokenLib} from "./libs/UniversalToken.sol"; import {Admin} from "./Admin.sol"; import {IFastBridge} from "./interfaces/IFastBridge.sol"; import {IFastBridgeV2} from "./interfaces/IFastBridgeV2.sol"; +import {IFastBridgeV2Errors} from "./interfaces/IFastBridgeV2Errors.sol"; -contract FastBridgeV2 is Admin, IFastBridgeV2 { +contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { using SafeERC20 for IERC20; using UniversalTokenLib for address; diff --git a/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2Errors.sol b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2Errors.sol new file mode 100644 index 0000000000..70fd3d0e39 --- /dev/null +++ b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2Errors.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IFastBridgeV2Errors { + error AmountIncorrect(); + error ChainIncorrect(); + error MsgValueIncorrect(); + error SenderIncorrect(); + error StatusIncorrect(); + error ZeroAddress(); + + error DeadlineExceeded(); + error DeadlineNotExceeded(); + error DeadlineTooShort(); + error DisputePeriodNotPassed(); + error DisputePeriodPassed(); + + error TransactionRelayed(); +} diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index d868a0738e..9fb2606e9f 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -24,7 +24,7 @@ "lint:check": "forge fmt --check && npm run solhint:check", "ci:lint": "npm run lint:check", "build:go": "./flatten.sh contracts/*.sol test/*.sol", - "solhint": "solhint '{contracts,script,test}/**/*.sol' --fix --noPrompt", - "solhint:check": "solhint '{contracts,script,test}/**/*.sol'" + "solhint": "solhint '{contracts,script,test}/**/*.sol' --fix --noPrompt --max-warnings 3", + "solhint:check": "solhint '{contracts,script,test}/**/*.sol' --max-warnings 3" } } diff --git a/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol index 926f05c8c0..b9957b85ca 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {ChainIncorrect, DeadlineExceeded, TransactionRelayed} from "../contracts/libs/Errors.sol"; - import {FastBridgeV2, FastBridgeV2Test, IFastBridge} from "./FastBridgeV2.t.sol"; // solhint-disable func-name-mixedcase, ordering diff --git a/packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol index d92e4f8ac5..dd4b3a4fa2 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol @@ -1,10 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.20; -import {FastBridgeTest, SenderIncorrect} from "./FastBridge.t.sol"; +import {IFastBridgeV2Errors} from "../contracts/interfaces/IFastBridgeV2Errors.sol"; + +import {FastBridgeTest} from "./FastBridge.t.sol"; // solhint-disable func-name-mixedcase, ordering -contract FastBridgeV2ParityTest is FastBridgeTest { +contract FastBridgeV2ParityTest is FastBridgeTest, IFastBridgeV2Errors { address public anotherRelayer = makeAddr("Another Relayer"); function deployFastBridge() internal virtual override returns (address) { diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol index 8a0106d0c0..b5177aae6a 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol @@ -1,19 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import { - AmountIncorrect, - ChainIncorrect, - DisputePeriodNotPassed, - DisputePeriodPassed, - DeadlineNotExceeded, - DeadlineTooShort, - MsgValueIncorrect, - SenderIncorrect, - StatusIncorrect, - ZeroAddress -} from "../contracts/libs/Errors.sol"; - import {FastBridgeV2, FastBridgeV2Test, IFastBridge} from "./FastBridgeV2.t.sol"; // solhint-disable func-name-mixedcase, ordering diff --git a/packages/contracts-rfq/test/FastBridgeV2.t.sol b/packages/contracts-rfq/test/FastBridgeV2.t.sol index 39881c5151..8517a2dbf8 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.t.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.20; import {IFastBridge} from "../contracts/interfaces/IFastBridge.sol"; +import {IFastBridgeV2Errors} from "../contracts/interfaces/IFastBridgeV2Errors.sol"; import {FastBridgeV2} from "../contracts/FastBridgeV2.sol"; import {MockERC20} from "./MockERC20.sol"; @@ -11,7 +12,7 @@ import {Test} from "forge-std/Test.sol"; import {stdStorage, StdStorage} from "forge-std/Test.sol"; // solhint-disable no-empty-blocks, ordering -abstract contract FastBridgeV2Test is Test { +abstract contract FastBridgeV2Test is Test, IFastBridgeV2Errors { using stdStorage for StdStorage; uint32 public constant SRC_CHAIN_ID = 1337; From 11ac6502799730a682345c30c11da9ff8f51de6e Mon Sep 17 00:00:00 2001 From: ChiTimesChi Date: Wed, 25 Sep 2024 11:33:32 +0000 Subject: [PATCH 25/90] Publish - @synapsecns/contracts-rfq@0.5.1 --- packages/contracts-rfq/CHANGELOG.md | 11 +++++++++++ packages/contracts-rfq/package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/contracts-rfq/CHANGELOG.md b/packages/contracts-rfq/CHANGELOG.md index e70355a860..5a569c7584 100644 --- a/packages/contracts-rfq/CHANGELOG.md +++ b/packages/contracts-rfq/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.0...@synapsecns/contracts-rfq@0.5.1) (2024-09-25) + + +### Bug Fixes + +* **contracts-rfq:** limit the amount of solhint warnings [SLT-245] ([#3182](https://github.com/synapsecns/sanguine/issues/3182)) ([c15ec86](https://github.com/synapsecns/sanguine/commit/c15ec8691902817f096589553bac360b53ba40cf)) + + + + + # 0.5.0 (2024-09-24) diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index 9fb2606e9f..eaf3c456be 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/contracts-rfq", "license": "MIT", - "version": "0.5.0", + "version": "0.5.1", "description": "FastBridge contracts.", "private": true, "files": [ From 8113d0bbf2ebaee1e31d8d1d593697963b13a031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CF=87=C2=B2?= <88190723+ChiTimesChi@users.noreply.github.com> Date: Wed, 25 Sep 2024 15:56:07 +0200 Subject: [PATCH 26/90] ci: Solidity gas diff [SLT-259] (#3181) * ci: run tests w/o coverage first for better visibility * test: malform the test to check the adjusted workflow * Revert "test: malform the test to check the adjusted workflow" This reverts commit e7db6e1848519c9025cc0d94a4971f473ecec284. * ci: add gas-diff workflow * try changing the contract to trigger gas diffs * retrigger the workflow * ci: provide the correct report path * ci: run on pull requests only * ci: save gas reports in monorepo root * Revert "ci: run on pull requests only" This reverts commit 0a01d609220b39fa83894c3eb64fe5516e6f7465. * Revert "try changing the contract to trigger gas diffs" This reverts commit 91bc03e420337fbddfb686573dda300e1c9e38c3. * refactor: wrap if statement * refactor: exclude `solidity-devops` package in a more generic way * ci: run tests w/o coverage for `solidity-devops`, add comments * add generic comment to trigger `solidity-devops` workflows * Revert "add generic comment to trigger `solidity-devops` workflows" This reverts commit cc35a4383b50e5e8186717b161f9d031c2913ba2. --- .github/workflows/solidity.yml | 70 ++++++++++++------- .../contracts-rfq/contracts/FastBridgeV2.sol | 1 + 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/.github/workflows/solidity.yml b/.github/workflows/solidity.yml index 499aa0bf98..a85fea4f88 100644 --- a/.github/workflows/solidity.yml +++ b/.github/workflows/solidity.yml @@ -110,7 +110,6 @@ jobs: env: VERCEL_PROJECT_ID: ${{ steps.project_id.outputs.VERCEL_PROJECT_ID}} - cancel-outdated: name: Cancel Outdated Jobs runs-on: ubuntu-latest @@ -130,6 +129,9 @@ jobs: fail-fast: false matrix: package: ${{ fromJson(needs.changes.outputs.packages) }} + # Slither is irrelevant for solidity-devops, as it only contains devops scripts rather than deployed contracts + exclude: + - package: solidity-devops permissions: # always required security-events: write @@ -138,32 +140,27 @@ jobs: contents: read steps: - name: Checkout repository - if: ${{ matrix.package != 'solidity-devops' }} uses: actions/checkout@v4 with: fetch-depth: 2 submodules: 'recursive' - name: Setup NodeJS - if: ${{ matrix.package != 'solidity-devops' }} uses: ./.github/actions/setup-nodejs - name: Install Foundry - if: ${{ matrix.package != 'solidity-devops' }} uses: foundry-rs/foundry-toolchain@v1 with: version: nightly # TODO: find a flag for this - name: Delete Untested Files - if: ${{ matrix.package != 'solidity-devops' }} working-directory: './packages/${{matrix.package}}' run: | rm -rf test/ || true rm -rf script/ || true - name: Build Contracts - if: ${{ matrix.package != 'solidity-devops' }} # TODO: unforunately, as of now concurrency needs to be 1 since if multiple instances of forge try to install the same version # of solc at the same time, we get "text file busy" errors. See https://github.com/synapsecns/sanguine/actions/runs/8457116792/job/23168392860?pr=2234 # for an example. @@ -171,7 +168,6 @@ jobs: npx lerna exec npm run build:slither --concurrency=1 - name: Run Slither - if: ${{ matrix.package != 'solidity-devops' }} uses: crytic/slither-action@v0.3.0 continue-on-error: true id: slither @@ -183,7 +179,7 @@ jobs: solc-version: 0.8.17 - name: Upload SARIF file - if: ${{!github.event.repository.private && matrix.package != 'solidity-devops'}} + if: ${{!github.event.repository.private}} uses: github/codeql-action/upload-sarif@v2 with: sarif_file: ./results.sarif @@ -199,28 +195,29 @@ jobs: package: ${{ fromJson(needs.changes.outputs.packages) }} steps: - uses: actions/checkout@v4 - if: ${{ matrix.package != 'solidity-devops' }} with: submodules: recursive - name: Setup Node JS - if: ${{ matrix.package != 'solidity-devops' }} uses: ./.github/actions/setup-nodejs - name: Installing dependencies - if: ${{ matrix.package != 'solidity-devops' }} run: yarn install --immutable - name: Install Foundry - if: ${{ matrix.package != 'solidity-devops' }} uses: foundry-rs/foundry-toolchain@v1 with: version: nightly - name: Run Foundry Tests + working-directory: './packages/${{matrix.package}}' + run: forge test -vvv + + # We skip only the coverage steps for solidity-devops before we establish a good coverage there + - name: Run Foundry Coverage if: ${{ matrix.package != 'solidity-devops' }} working-directory: './packages/${{matrix.package}}' - run: forge coverage -vvv --report lcov --report summary >> $GITHUB_STEP_SUMMARY + run: forge coverage --report lcov --report summary >> $GITHUB_STEP_SUMMARY - name: Send Coverage (Codecov) if: ${{ matrix.package != 'solidity-devops' }} @@ -236,39 +233,59 @@ jobs: attempt_limit: 5 attempt_delay: 30000 - - snapshot: + gas-diff: runs-on: ubuntu-latest - name: Foundry Gas Snapshot + name: Foundry Gas Diff if: ${{ needs.changes.outputs.package_count > 0 }} needs: changes strategy: fail-fast: false matrix: package: ${{ fromJson(needs.changes.outputs.packages) }} + # Gas diff is irrelevant for solidity-devops, as it only contains devops scripts rather than deployed contracts + exclude: + - package: solidity-devops steps: - uses: actions/checkout@v4 - if: ${{ matrix.package != 'solidity-devops' }} with: submodules: recursive - name: Setup Node JS - if: ${{ matrix.package != 'solidity-devops' }} uses: ./.github/actions/setup-nodejs - name: Installing dependencies - if: ${{ matrix.package != 'solidity-devops' }} run: yarn install --immutable - name: Install Foundry - if: ${{ matrix.package != 'solidity-devops' }} uses: foundry-rs/foundry-toolchain@v1 with: version: nightly - - name: Run snapshot - if: ${{ matrix.package != 'solidity-devops' }} + + # TODO: consider defining a package-specific script for this + - name: Run tests and generate gas report working-directory: './packages/${{matrix.package}}' - run: forge snapshot >> $GITHUB_STEP_SUMMARY + # Excluding tests with reverts to get accurate average gas cost estimates + run: forge test --nmt "(fail|revert)" --gas-report > "../../gas-report-${{ matrix.package }}.ansi" + env: + # make fuzzing semi-deterministic to avoid noisy gas cost estimation + # due to non-deterministic fuzzing (but still use pseudo-random fuzzing seeds) + FOUNDRY_FUZZ_SEED: 0x${{ github.event.pull_request.base.sha || github.sha }} + + - name: Compare gas reports + uses: Rubilmax/foundry-gas-diff@v3.18 + # TODO: define some options? + with: + report: 'gas-report-${{ matrix.package }}.ansi' + id: gas_diff + + - name: Add gas diff to sticky comment + if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }} + uses: marocchino/sticky-pull-request-comment@v2 + with: + # delete the comment in case changes no longer impact gas costs + delete: ${{ !steps.gas_diff.outputs.markdown }} + message: ${{ steps.gas_diff.outputs.markdown }} + size-check: name: Foundry Size Check runs-on: ubuntu-latest @@ -278,25 +295,24 @@ jobs: fail-fast: false matrix: package: ${{ fromJson(needs.changes.outputs.packages) }} + # Size check is irrelevant for solidity-devops, as it only contains devops scripts rather than deployed contracts + exclude: + - package: solidity-devops steps: - uses: actions/checkout@v4 - if: ${{ matrix.package != 'solidity-devops' }} with: submodules: recursive - name: Setup Node JS - if: ${{ matrix.package != 'solidity-devops' }} uses: ./.github/actions/setup-nodejs - name: Install Foundry - if: ${{ matrix.package != 'solidity-devops' }} uses: foundry-rs/foundry-toolchain@v1 with: version: nightly # This will run https://book.getfoundry.sh/reference/forge/forge-build#build-options - name: Run forge build --sizes - if: ${{ matrix.package != 'solidity-devops' }} run: | forge build --sizes working-directory: './packages/${{matrix.package}}' diff --git a/packages/contracts-rfq/contracts/FastBridgeV2.sol b/packages/contracts-rfq/contracts/FastBridgeV2.sol index 50acfca74c..abaf4810b2 100644 --- a/packages/contracts-rfq/contracts/FastBridgeV2.sol +++ b/packages/contracts-rfq/contracts/FastBridgeV2.sol @@ -10,6 +10,7 @@ import {IFastBridge} from "./interfaces/IFastBridge.sol"; import {IFastBridgeV2} from "./interfaces/IFastBridgeV2.sol"; import {IFastBridgeV2Errors} from "./interfaces/IFastBridgeV2Errors.sol"; +/// @notice FastBridgeV2 is a contract for bridging tokens across chains. contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { using SafeERC20 for IERC20; using UniversalTokenLib for address; From e89b363a90381574cfcaefd4fdc622b3d27d73f6 Mon Sep 17 00:00:00 2001 From: ChiTimesChi Date: Wed, 25 Sep 2024 14:00:18 +0000 Subject: [PATCH 27/90] Publish - @synapsecns/contracts-rfq@0.5.2 --- packages/contracts-rfq/CHANGELOG.md | 8 ++++++++ packages/contracts-rfq/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/contracts-rfq/CHANGELOG.md b/packages/contracts-rfq/CHANGELOG.md index 5a569c7584..ebbb37742a 100644 --- a/packages/contracts-rfq/CHANGELOG.md +++ b/packages/contracts-rfq/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.1...@synapsecns/contracts-rfq@0.5.2) (2024-09-25) + +**Note:** Version bump only for package @synapsecns/contracts-rfq + + + + + ## [0.5.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.0...@synapsecns/contracts-rfq@0.5.1) (2024-09-25) diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index eaf3c456be..27fa88de91 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/contracts-rfq", "license": "MIT", - "version": "0.5.1", + "version": "0.5.2", "description": "FastBridge contracts.", "private": true, "files": [ From 75941008756963866cb722c7c21dc81da18ac256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CF=87=C2=B2?= <88190723+ChiTimesChi@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:42:23 +0200 Subject: [PATCH 28/90] fix(contracts-core): set very high gas limit for intensive tests [SLT-259] (#3186) * fix: set very high gas limit for intensive tests * ci: speed up solidity coverage --- .github/workflows/solidity.yml | 3 +++ packages/contracts-core/foundry.toml | 2 ++ 2 files changed, 5 insertions(+) diff --git a/.github/workflows/solidity.yml b/.github/workflows/solidity.yml index a85fea4f88..4e4b82e6c6 100644 --- a/.github/workflows/solidity.yml +++ b/.github/workflows/solidity.yml @@ -218,6 +218,9 @@ jobs: if: ${{ matrix.package != 'solidity-devops' }} working-directory: './packages/${{matrix.package}}' run: forge coverage --report lcov --report summary >> $GITHUB_STEP_SUMMARY + # Limit the number of fuzz runs to speed up the coverage + env: + FOUNDRY_FUZZ_RUNS: 10 - name: Send Coverage (Codecov) if: ${{ matrix.package != 'solidity-devops' }} diff --git a/packages/contracts-core/foundry.toml b/packages/contracts-core/foundry.toml index f6be41d854..26d634bcd7 100644 --- a/packages/contracts-core/foundry.toml +++ b/packages/contracts-core/foundry.toml @@ -6,6 +6,8 @@ src = "contracts" out = "artifacts" libs = ["../../node_modules", "node_modules", "lib"] fs_permissions = [{ access = "read", path = "./artifacts"}, { access = "read-write", path = "./deployments"}, {access = "read-write", path = "./script"}] +# https://book.getfoundry.sh/reference/config/testing#gas_limit +gas_limit = "18446744073709551615" [profile.ci] verbosity = 4 From 6ec996d036b6a424639fe80209ef1aa283731890 Mon Sep 17 00:00:00 2001 From: ChiTimesChi Date: Wed, 25 Sep 2024 14:46:32 +0000 Subject: [PATCH 29/90] Publish - @synapsecns/contracts-core@1.0.34 --- packages/contracts-core/CHANGELOG.md | 11 +++++++++++ packages/contracts-core/package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/contracts-core/CHANGELOG.md b/packages/contracts-core/CHANGELOG.md index 266f1aacf3..acf8984269 100644 --- a/packages/contracts-core/CHANGELOG.md +++ b/packages/contracts-core/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.0.34](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-core@1.0.33...@synapsecns/contracts-core@1.0.34) (2024-09-25) + + +### Bug Fixes + +* **contracts-core:** set very high gas limit for intensive tests [SLT-259] ([#3186](https://github.com/synapsecns/sanguine/issues/3186)) ([7594100](https://github.com/synapsecns/sanguine/commit/75941008756963866cb722c7c21dc81da18ac256)) + + + + + ## [1.0.33](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-core@1.0.32...@synapsecns/contracts-core@1.0.33) (2024-09-24) diff --git a/packages/contracts-core/package.json b/packages/contracts-core/package.json index 7413177aae..c8e45bfd18 100644 --- a/packages/contracts-core/package.json +++ b/packages/contracts-core/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/contracts-core", - "version": "1.0.33", + "version": "1.0.34", "description": "", "scripts": { "build": "yarn build:contracts && yarn build:typescript && yarn build:go", From ceff8bcbc179dd6442cd1861eb31505217f9c55c Mon Sep 17 00:00:00 2001 From: abtestingalpha <104046418+abtestingalpha@users.noreply.github.com> Date: Wed, 25 Sep 2024 14:42:49 -0400 Subject: [PATCH 30/90] feat(rest-api): Adds validateRouteExists validation [SLT-260] (#3180) * Adds validateRouteExists validation * Remove timeouts for 400s --- .../rest-api/src/routes/bridgeLimitsRoute.ts | 8 ++++++ packages/rest-api/src/routes/bridgeRoute.ts | 8 ++++++ .../rest-api/src/routes/bridgeTxInfoRoute.ts | 8 ++++++ .../src/tests/bridgeLimitsRoute.test.ts | 23 +++++++++++++--- .../rest-api/src/tests/bridgeRoute.test.ts | 27 +++++++++++++++---- .../src/tests/bridgeTxInfoRoute.test.ts | 27 +++++++++++++++---- .../rest-api/src/utils/bridgeRouteMapping.ts | 10 ++++--- .../src/validations/validateRouteExists.ts | 20 ++++++++++++++ .../src/validations/validateTokens.ts | 22 --------------- 9 files changed, 113 insertions(+), 40 deletions(-) create mode 100644 packages/rest-api/src/validations/validateRouteExists.ts delete mode 100644 packages/rest-api/src/validations/validateTokens.ts diff --git a/packages/rest-api/src/routes/bridgeLimitsRoute.ts b/packages/rest-api/src/routes/bridgeLimitsRoute.ts index 14ab637671..392937ac28 100644 --- a/packages/rest-api/src/routes/bridgeLimitsRoute.ts +++ b/packages/rest-api/src/routes/bridgeLimitsRoute.ts @@ -8,6 +8,7 @@ import { isTokenSupportedOnChain } from './../utils/isTokenSupportedOnChain' import { isTokenAddress } from '../utils/isTokenAddress' import { normalizeNativeTokenAddress } from '../middleware/normalizeNativeTokenAddress' import { checksumAddresses } from '../middleware/checksumAddresses' +import { validateRouteExists } from '../validations/validateRouteExists' const router = express.Router() @@ -130,6 +131,13 @@ router.get( isTokenSupportedOnChain(value, req.query.toChain as string) ) .withMessage('Token not supported on specified chain'), + check() + .custom((_value, { req }) => { + const { fromChain, toChain, fromToken, toToken } = req.query + + return validateRouteExists(fromChain, fromToken, toChain, toToken) + }) + .withMessage('No valid route exists for the chain/token combination'), ], showFirstValidationError, bridgeLimitsController diff --git a/packages/rest-api/src/routes/bridgeRoute.ts b/packages/rest-api/src/routes/bridgeRoute.ts index 01fccb708b..0e4a88b947 100644 --- a/packages/rest-api/src/routes/bridgeRoute.ts +++ b/packages/rest-api/src/routes/bridgeRoute.ts @@ -8,6 +8,7 @@ import { bridgeController } from '../controllers/bridgeController' import { isTokenSupportedOnChain } from '../utils/isTokenSupportedOnChain' import { checksumAddresses } from '../middleware/checksumAddresses' import { normalizeNativeTokenAddress } from '../middleware/normalizeNativeTokenAddress' +import { validateRouteExists } from '../validations/validateRouteExists' const router = express.Router() @@ -222,6 +223,13 @@ router.get( ) .withMessage('Token not supported on specified chain'), check('amount').isNumeric().exists().withMessage('amount is required'), + check() + .custom((_value, { req }) => { + const { fromChain, toChain, fromToken, toToken } = req.query + + return validateRouteExists(fromChain, fromToken, toChain, toToken) + }) + .withMessage('No valid route exists for the chain/token combination'), ], showFirstValidationError, bridgeController diff --git a/packages/rest-api/src/routes/bridgeTxInfoRoute.ts b/packages/rest-api/src/routes/bridgeTxInfoRoute.ts index 09f249b99b..5a0ce30326 100644 --- a/packages/rest-api/src/routes/bridgeTxInfoRoute.ts +++ b/packages/rest-api/src/routes/bridgeTxInfoRoute.ts @@ -9,6 +9,7 @@ import { isTokenAddress } from '../utils/isTokenAddress' import { isTokenSupportedOnChain } from '../utils/isTokenSupportedOnChain' import { checksumAddresses } from '../middleware/checksumAddresses' import { normalizeNativeTokenAddress } from '../middleware/normalizeNativeTokenAddress' +import { validateRouteExists } from '../validations/validateRouteExists' const router = express.Router() @@ -163,6 +164,13 @@ router.get( .withMessage('destAddress is required') .custom((value) => isAddress(value)) .withMessage('Invalid destination address'), + check() + .custom((_value, { req }) => { + const { fromChain, toChain, fromToken, toToken } = req.query + + return validateRouteExists(fromChain, fromToken, toChain, toToken) + }) + .withMessage('No valid route exists for the chain/token combination'), ], showFirstValidationError, bridgeTxInfoController diff --git a/packages/rest-api/src/tests/bridgeLimitsRoute.test.ts b/packages/rest-api/src/tests/bridgeLimitsRoute.test.ts index 921393773b..9e68a5d20e 100644 --- a/packages/rest-api/src/tests/bridgeLimitsRoute.test.ts +++ b/packages/rest-api/src/tests/bridgeLimitsRoute.test.ts @@ -3,6 +3,7 @@ import express from 'express' import bridgeLimitsRoute from '../routes/bridgeLimitsRoute' import { USDC, ETH } from '../constants/bridgeable' +import { NativeGasAddress } from '../constants' const app = express() app.use('/bridgeLimits', bridgeLimitsRoute) @@ -34,6 +35,20 @@ describe('Get Bridge Limits Route', () => { expect(response.body).toHaveProperty('minOriginAmount') }, 10_000) + it('should return 400 for unsupported route', async () => { + const response = await request(app).get('/bridgeLimits').query({ + fromChain: '1', + toChain: '10', + fromToken: NativeGasAddress, + toToken: USDC.addresses[10], + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty( + 'message', + 'No valid route exists for the chain/token combination' + ) + }) + it('should return 400 for unsupported fromChain', async () => { const response = await request(app).get('/bridgeLimits').query({ fromChain: '999', @@ -46,7 +61,7 @@ describe('Get Bridge Limits Route', () => { 'message', 'Unsupported fromChain' ) - }, 10_000) + }) it('should return 400 for unsupported toChain', async () => { const response = await request(app).get('/bridgeLimits').query({ @@ -57,7 +72,7 @@ describe('Get Bridge Limits Route', () => { }) expect(response.status).toBe(400) expect(response.body.error).toHaveProperty('message', 'Unsupported toChain') - }, 10_000) + }) it('should return 400 for missing fromToken', async () => { const response = await request(app).get('/bridgeLimits').query({ @@ -67,7 +82,7 @@ describe('Get Bridge Limits Route', () => { }) expect(response.status).toBe(400) expect(response.body.error).toHaveProperty('field', 'fromToken') - }, 10_000) + }) it('should return 400 for missing toToken', async () => { const response = await request(app).get('/bridgeLimits').query({ @@ -77,5 +92,5 @@ describe('Get Bridge Limits Route', () => { }) expect(response.status).toBe(400) expect(response.body.error).toHaveProperty('field', 'toToken') - }, 10_000) + }) }) diff --git a/packages/rest-api/src/tests/bridgeRoute.test.ts b/packages/rest-api/src/tests/bridgeRoute.test.ts index 756089f50b..3617d5d71b 100644 --- a/packages/rest-api/src/tests/bridgeRoute.test.ts +++ b/packages/rest-api/src/tests/bridgeRoute.test.ts @@ -17,6 +17,7 @@ describe('Bridge Route with Real Synapse Service', () => { toToken: USDC.addresses[10], amount: '1000', }) + expect(response.status).toBe(200) expect(Array.isArray(response.body)).toBe(true) expect(response.body.length).toBeGreaterThan(0) @@ -55,6 +56,22 @@ describe('Bridge Route with Real Synapse Service', () => { expect(response.body[0]).toHaveProperty('bridgeFeeFormatted') }, 15000) + it('should return 400 for unsupported route', async () => { + const response = await request(app).get('/bridge').query({ + fromChain: '1', + toChain: '10', + fromToken: NativeGasAddress, + toToken: USDC.addresses[10], + amount: '10', + }) + + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty( + 'message', + 'No valid route exists for the chain/token combination' + ) + }) + it('should return 400 for unsupported fromChain, with error message', async () => { const response = await request(app).get('/bridge').query({ fromChain: '999', @@ -68,7 +85,7 @@ describe('Bridge Route with Real Synapse Service', () => { 'message', 'Unsupported fromChain' ) - }, 10000) + }) it('should return 400 for unsupported toChain, with error message', async () => { const response = await request(app).get('/bridge').query({ @@ -80,7 +97,7 @@ describe('Bridge Route with Real Synapse Service', () => { }) expect(response.status).toBe(400) expect(response.body.error).toHaveProperty('message', 'Unsupported toChain') - }, 10000) + }) it('should return 400 for invalid fromToken address, with error message', async () => { const response = await request(app).get('/bridge').query({ @@ -95,7 +112,7 @@ describe('Bridge Route with Real Synapse Service', () => { 'message', 'Invalid fromToken address' ) - }, 10000) + }) it('should return 400 for token not supported on specified chain, with error message', async () => { const response = await request(app).get('/bridge').query({ @@ -110,7 +127,7 @@ describe('Bridge Route with Real Synapse Service', () => { 'message', 'Invalid fromToken address' ) - }, 10000) + }) it('should return 400 for missing amount, with error message', async () => { const response = await request(app).get('/bridge').query({ @@ -121,5 +138,5 @@ describe('Bridge Route with Real Synapse Service', () => { }) expect(response.status).toBe(400) expect(response.body.error).toHaveProperty('field', 'amount') - }, 10000) + }) }) diff --git a/packages/rest-api/src/tests/bridgeTxInfoRoute.test.ts b/packages/rest-api/src/tests/bridgeTxInfoRoute.test.ts index 822491f887..9f5d183bbe 100644 --- a/packages/rest-api/src/tests/bridgeTxInfoRoute.test.ts +++ b/packages/rest-api/src/tests/bridgeTxInfoRoute.test.ts @@ -3,6 +3,7 @@ import express from 'express' import bridgeTxInfoRoute from '../routes/bridgeTxInfoRoute' import { USDC } from '../constants/bridgeable' +import { NativeGasAddress } from '../constants' const app = express() app.use('/bridgeTxInfo', bridgeTxInfoRoute) @@ -28,6 +29,22 @@ describe('Bridge TX Info Route', () => { ) }, 10_000) + it('should return 400 for unsupported route', async () => { + const response = await request(app).get('/bridgeTxInfo').query({ + fromChain: '1', + toChain: '10', + fromToken: NativeGasAddress, + toToken: USDC.addresses[10], + amount: '10', + destAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty( + 'message', + 'No valid route exists for the chain/token combination' + ) + }) + it('should return 400 for unsupported fromChain', async () => { const response = await request(app).get('/bridgeTxInfo').query({ fromChain: '999', @@ -42,7 +59,7 @@ describe('Bridge TX Info Route', () => { 'message', 'Unsupported fromChain' ) - }, 10_000) + }) it('should return 400 for invalid fromToken address', async () => { const response = await request(app).get('/bridgeTxInfo').query({ @@ -58,7 +75,7 @@ describe('Bridge TX Info Route', () => { 'message', 'Invalid fromToken address' ) - }, 10_000) + }) it('should return 400 for token not supported on specified chain', async () => { const response = await request(app).get('/bridgeTxInfo').query({ @@ -74,7 +91,7 @@ describe('Bridge TX Info Route', () => { 'message', 'Invalid fromToken address' ) - }, 10_000) + }) it('should return 400 for missing amount', async () => { const response = await request(app).get('/bridgeTxInfo').query({ @@ -86,7 +103,7 @@ describe('Bridge TX Info Route', () => { }) expect(response.status).toBe(400) expect(response.body.error).toHaveProperty('field', 'amount') - }, 10_000) + }) it('should return 400 for invalid destAddress', async () => { const response = await request(app).get('/bridgeTxInfo').query({ @@ -102,5 +119,5 @@ describe('Bridge TX Info Route', () => { 'message', 'Invalid destination address' ) - }, 10_000) + }) }) diff --git a/packages/rest-api/src/utils/bridgeRouteMapping.ts b/packages/rest-api/src/utils/bridgeRouteMapping.ts index 996652e543..992ccffe0f 100644 --- a/packages/rest-api/src/utils/bridgeRouteMapping.ts +++ b/packages/rest-api/src/utils/bridgeRouteMapping.ts @@ -13,7 +13,7 @@ type TransformedBridgeRoutes = Record const constructJSON = ( swappableMap, exclusionList -): TransformedBridgeRoutes => { +): StringifiedBridgeRoutes => { const result = {} // Iterate through the chains @@ -56,8 +56,7 @@ const constructJSON = ( } } } - - return transformBridgeRouteValues(result) + return result } const transformPair = (string: string): any => { @@ -97,4 +96,7 @@ const transformBridgeRouteValues = ( ) } -export const BRIDGE_ROUTE_MAPPING = constructJSON(BRIDGE_MAP, []) +export const BRIDGE_ROUTE_MAPPING_SYMBOLS = constructJSON(BRIDGE_MAP, []) +export const BRIDGE_ROUTE_MAPPING = transformBridgeRouteValues( + BRIDGE_ROUTE_MAPPING_SYMBOLS +) diff --git a/packages/rest-api/src/validations/validateRouteExists.ts b/packages/rest-api/src/validations/validateRouteExists.ts new file mode 100644 index 0000000000..4339d9d8c1 --- /dev/null +++ b/packages/rest-api/src/validations/validateRouteExists.ts @@ -0,0 +1,20 @@ +import { tokenAddressToToken } from '../utils/tokenAddressToToken' +import { BRIDGE_ROUTE_MAPPING_SYMBOLS } from '../utils/bridgeRouteMapping' + +export const validateRouteExists = (fromChain, fromToken, toChain, toToken) => { + const fromTokenInfo = tokenAddressToToken(fromChain.toString(), fromToken) + const toTokenInfo = tokenAddressToToken(toChain.toString(), toToken) + + if (!fromTokenInfo || !toTokenInfo) { + return false + } + + const key = `${fromTokenInfo.symbol}-${fromChain}` + const routes = BRIDGE_ROUTE_MAPPING_SYMBOLS[key] + + if (!routes) { + return false + } + + return routes.includes(`${toTokenInfo.symbol}-${toChain}`) +} diff --git a/packages/rest-api/src/validations/validateTokens.ts b/packages/rest-api/src/validations/validateTokens.ts deleted file mode 100644 index 6e89a9c70c..0000000000 --- a/packages/rest-api/src/validations/validateTokens.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { check } from 'express-validator' - -import { tokenSymbolToToken } from '../utils/tokenSymbolToToken' - -export const validateTokens = (chainParam, tokenParam, paramName) => { - return check(tokenParam) - .isString() - .exists() - .withMessage(`${paramName} is required`) - .custom((value, { req }) => { - const chain = req.query[chainParam] - const tokenInfo = tokenSymbolToToken(chain, value) - if (!tokenInfo) { - throw new Error(`Invalid ${paramName} symbol`) - } - if (!req.res.locals.tokenInfo) { - req.res.locals.tokenInfo = {} - } - req.res.locals.tokenInfo[paramName] = tokenInfo - return true - }) -} From 3af343816ce7e849c27f272d25b539796a48a394 Mon Sep 17 00:00:00 2001 From: abtestingalpha Date: Wed, 25 Sep 2024 18:47:03 +0000 Subject: [PATCH 31/90] Publish - @synapsecns/rest-api@1.3.0 --- packages/rest-api/CHANGELOG.md | 11 +++++++++++ packages/rest-api/package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/rest-api/CHANGELOG.md b/packages/rest-api/CHANGELOG.md index ac53ec49bf..b56508ac64 100644 --- a/packages/rest-api/CHANGELOG.md +++ b/packages/rest-api/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.3.0](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.2.0...@synapsecns/rest-api@1.3.0) (2024-09-25) + + +### Features + +* **rest-api:** Adds validateRouteExists validation [SLT-260] ([#3180](https://github.com/synapsecns/sanguine/issues/3180)) ([ceff8bc](https://github.com/synapsecns/sanguine/commit/ceff8bcbc179dd6442cd1861eb31505217f9c55c)) + + + + + # [1.2.0](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.1.5...@synapsecns/rest-api@1.2.0) (2024-09-24) diff --git a/packages/rest-api/package.json b/packages/rest-api/package.json index e6836b39f0..7f52b3613e 100644 --- a/packages/rest-api/package.json +++ b/packages/rest-api/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/rest-api", - "version": "1.2.0", + "version": "1.3.0", "private": "true", "engines": { "node": ">=18.17.0" From 3e75b0db3aee289e43800107197649f6560272bb Mon Sep 17 00:00:00 2001 From: trajan0x <83933037+trajan0x@users.noreply.github.com> Date: Wed, 25 Sep 2024 20:58:35 -0400 Subject: [PATCH 32/90] add duplicate command warning (#3174) Co-authored-by: Trajan0x --- core/commandline/shell.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/commandline/shell.go b/core/commandline/shell.go index ef5f10b2da..ff9e5dd1a3 100644 --- a/core/commandline/shell.go +++ b/core/commandline/shell.go @@ -3,6 +3,7 @@ package commandline import ( "context" "fmt" + "k8s.io/apimachinery/pkg/util/sets" "os" "os/signal" "os/user" @@ -90,11 +91,18 @@ func GenerateShellCommand(shellCommands []*cli.Command) *cli.Command { // pruneShellCommands gets a list of commands including the shell command. func pruneShellCommands(commands []*cli.Command) (prunedCommands []*cli.Command) { // initialize shell commands + nameSet := sets.NewString() for _, command := range commands { if command.Name != shellCommandName { prunedCommands = append(prunedCommands, command) } + if !nameSet.Has(command.Name) { + fmt.Printf("Command %s already exists, skipping\n", command.Name) + } + + nameSet.Insert(command.Name) } + return prunedCommands } From 2e808e6414be03d63c1d6785ef4ce9b1c64a4775 Mon Sep 17 00:00:00 2001 From: parodime Date: Thu, 26 Sep 2024 05:19:39 -0400 Subject: [PATCH 33/90] reduce solhint warnings on FbV2 (#3189) * reduce solhint warnings on FbV2 * fix whitespace --- packages/contracts-rfq/contracts/FastBridgeV2.sol | 3 ++- packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/contracts-rfq/contracts/FastBridgeV2.sol b/packages/contracts-rfq/contracts/FastBridgeV2.sol index abaf4810b2..4ea19df8d5 100644 --- a/packages/contracts-rfq/contracts/FastBridgeV2.sol +++ b/packages/contracts-rfq/contracts/FastBridgeV2.sol @@ -186,7 +186,8 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { // update bridge tx status given proof provided if (bridgeStatuses[transactionId] != BridgeStatus.REQUESTED) revert StatusIncorrect(); bridgeStatuses[transactionId] = BridgeStatus.RELAYER_PROVED; - bridgeProofs[transactionId] = BridgeProof({timestamp: uint96(block.timestamp), relayer: relayer}); // overflow ok + // overflow ok + bridgeProofs[transactionId] = BridgeProof({timestamp: uint96(block.timestamp), relayer: relayer}); emit BridgeProofProvided(transactionId, relayer, destTxHash); } diff --git a/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol index 2002a4ba14..63154b6255 100644 --- a/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol +++ b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol @@ -15,7 +15,8 @@ interface IFastBridgeV2 is IFastBridge { /// @param relayer The address of the relaying entity which should have control of the origin funds when claimed function prove(bytes memory request, bytes32 destTxHash, address relayer) external; - /// @notice Completes bridge transaction on origin chain by claiming originally deposited capital. Can only send funds to the relayer address on the proof. + /// @notice Completes bridge transaction on origin chain by claiming originally deposited capital. + /// @notice Can only send funds to the relayer address on the proof. /// @param request The encoded bridge transaction to claim on origin chain function claim(bytes memory request) external; } From 1bdfee61c2c18d001cc5361f483bc41dfb3bdece Mon Sep 17 00:00:00 2001 From: parodime Date: Thu, 26 Sep 2024 09:23:53 +0000 Subject: [PATCH 34/90] Publish - @synapsecns/contracts-rfq@0.5.3 --- packages/contracts-rfq/CHANGELOG.md | 8 ++++++++ packages/contracts-rfq/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/contracts-rfq/CHANGELOG.md b/packages/contracts-rfq/CHANGELOG.md index ebbb37742a..6b91e97f87 100644 --- a/packages/contracts-rfq/CHANGELOG.md +++ b/packages/contracts-rfq/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.3](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.2...@synapsecns/contracts-rfq@0.5.3) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/contracts-rfq + + + + + ## [0.5.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.1...@synapsecns/contracts-rfq@0.5.2) (2024-09-25) **Note:** Version bump only for package @synapsecns/contracts-rfq diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index 27fa88de91..979c16adad 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/contracts-rfq", "license": "MIT", - "version": "0.5.2", + "version": "0.5.3", "description": "FastBridge contracts.", "private": true, "files": [ From 2709e85e22d4ec59fd30bcd8eeb3a4111a4c7c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CF=87=C2=B2?= <88190723+ChiTimesChi@users.noreply.github.com> Date: Thu, 26 Sep 2024 11:50:04 +0100 Subject: [PATCH 35/90] ci: solidity gas diff options [SLT-267] (#3193) * ci: ignore test files in gas diff report * add some changes to the test files * ci: define some options for gas-diff * try changing the contract to trigger gas diffs * Revert "try changing the contract to trigger gas diffs" This reverts commit 4504e3cbea0f767e7acf9e72a21795f4bac045ae. * Revert "add some changes to the test files" This reverts commit 7e7d6cba49f298be590d6f3302fb9f43dcb0f7ae. --- .github/workflows/solidity.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/solidity.yml b/.github/workflows/solidity.yml index 4e4b82e6c6..ee44b8d940 100644 --- a/.github/workflows/solidity.yml +++ b/.github/workflows/solidity.yml @@ -276,9 +276,12 @@ jobs: - name: Compare gas reports uses: Rubilmax/foundry-gas-diff@v3.18 - # TODO: define some options? with: + ignore: 'test/**/*' report: 'gas-report-${{ matrix.package }}.ansi' + sortCriteria: avg + sortOrders: desc + summaryQuantile: 0.5 id: gas_diff - name: Add gas diff to sticky comment From 9742f8f8d3fae9551c011d7644c44be1b02f01d9 Mon Sep 17 00:00:00 2001 From: parodime Date: Thu, 26 Sep 2024 07:19:18 -0400 Subject: [PATCH 36/90] prove w/ tx id [SLT-181] (#3169) * prove w/ tx id SLT-181 * +proveOther tests, forge fmt * fmt * fmt --- .../contracts-rfq/contracts/FastBridgeV2.sol | 6 +- .../contracts/interfaces/IFastBridgeV2.sol | 4 +- .../contracts-rfq/test/FastBridgeV2.Src.t.sol | 130 ++++++++++++++++++ 3 files changed, 135 insertions(+), 5 deletions(-) diff --git a/packages/contracts-rfq/contracts/FastBridgeV2.sol b/packages/contracts-rfq/contracts/FastBridgeV2.sol index 4ea19df8d5..99c3568de1 100644 --- a/packages/contracts-rfq/contracts/FastBridgeV2.sol +++ b/packages/contracts-rfq/contracts/FastBridgeV2.sol @@ -177,12 +177,12 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { /// @inheritdoc IFastBridge function prove(bytes memory request, bytes32 destTxHash) external { - prove(request, destTxHash, msg.sender); + bytes32 transactionId = keccak256(request); + prove(transactionId, destTxHash, msg.sender); } /// @inheritdoc IFastBridgeV2 - function prove(bytes memory request, bytes32 destTxHash, address relayer) public onlyRole(RELAYER_ROLE) { - bytes32 transactionId = keccak256(request); + function prove(bytes32 transactionId, bytes32 destTxHash, address relayer) public onlyRole(RELAYER_ROLE) { // update bridge tx status given proof provided if (bridgeStatuses[transactionId] != BridgeStatus.REQUESTED) revert StatusIncorrect(); bridgeStatuses[transactionId] = BridgeStatus.RELAYER_PROVED; diff --git a/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol index 63154b6255..0d9cd33fcb 100644 --- a/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol +++ b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol @@ -10,10 +10,10 @@ interface IFastBridgeV2 is IFastBridge { function relay(bytes memory request, address relayer) external payable; /// @notice Provides proof on origin side that relayer provided funds on destination side of bridge transaction - /// @param request The encoded bridge transaction to prove on origin chain + /// @param transactionId The transaction id associated with the encoded bridge transaction to prove /// @param destTxHash The destination tx hash proving bridge transaction was relayed /// @param relayer The address of the relaying entity which should have control of the origin funds when claimed - function prove(bytes memory request, bytes32 destTxHash, address relayer) external; + function prove(bytes32 transactionId, bytes32 destTxHash, address relayer) external; /// @notice Completes bridge transaction on origin chain by claiming originally deposited capital. /// @notice Can only send funds to the relayer address on the proof. diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol index b5177aae6a..6e433a1274 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol @@ -75,6 +75,11 @@ contract FastBridgeV2SrcTest is FastBridgeV2Test { fastBridge.bridge{value: msgValue}(params); } + function prove(address caller, bytes32 transactionId, bytes32 destTxHash, address relayer) public { + vm.prank(caller); + fastBridge.prove(transactionId, destTxHash, relayer); + } + function prove(address caller, IFastBridge.BridgeTransaction memory bridgeTx, bytes32 destTxHash) public { vm.prank(caller); fastBridge.prove(abi.encode(bridgeTx), destTxHash); @@ -353,6 +358,131 @@ contract FastBridgeV2SrcTest is FastBridgeV2Test { prove({caller: caller, bridgeTx: tokenTx, destTxHash: hex"01"}); } + // ════════════════════════════════════════ PROVE OTHER RELAYER ════════════════════════════════════════════ + + function test_proveOther_token() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"}); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"01", relayer: relayerA}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + function test_proveOther_eth() public { + // bridge token first to match the nonce + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"}); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"01", relayer: relayerA}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethParams.originAmount); + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); + } + + // relayer self-proving using tx id, which is capable of proving for another & most tests focus on that angle. + function test_proveOther_self() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"}); + prove({caller: relayerA, transactionId: txId, destTxHash: hex"01", relayer: relayerA}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + // arbitrary non-privileged address can be asserted as the relayer + function test_proveOther_permless() public { + bytes32 txId = getTxId(tokenTx); + + bridge({caller: userA, msgValue: 0, params: tokenParams}); + expectBridgeProofProvided({txId: txId, relayer: address(0x1234), destTxHash: hex"01"}); + prove({caller: relayerA, transactionId: txId, destTxHash: hex"01", relayer: address(0x1234)}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, address(0x1234)); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + function test_proveOther_reProveAfterDispute() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"}); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"01", relayer: relayerA}); + expectBridgeProofDisputed(txId, guard); + dispute(guard, txId); + expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"02"}); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"02", relayer: relayerA}); + expectBridgeProofDisputed(txId, guard); + dispute(guard, txId); + expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"03"}); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"03", relayer: relayerA}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + // can prove long after relaying as long as status is still good + function test_proveOther_longDelay() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(10 days); + expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"}); + prove({caller: relayerA, transactionId: txId, destTxHash: hex"01", relayer: relayerA}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + function test_proveOther_revert_statusProved() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"01", relayer: relayerA}); + vm.expectRevert(StatusIncorrect.selector); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"02", relayer: relayerA}); + } + + function test_proveOther_revert_statusClaimed() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"01", relayer: relayerA}); + skip(CLAIM_DELAY + 1); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + vm.expectRevert(StatusIncorrect.selector); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"02", relayer: relayerA}); + } + + function test_proveOther_revert_statusRefunded() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE + 1); + refund({caller: refunder, bridgeTx: tokenTx}); + vm.expectRevert(StatusIncorrect.selector); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"01", relayer: relayerA}); + } + + function test_proveOther_revert_callerNotAuthed(address caller) public { + bytes32 txId = getTxId(tokenTx); + vm.assume(caller != relayerA && caller != relayerB); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + expectUnauthorized(caller, fastBridge.RELAYER_ROLE()); + prove({caller: caller, transactionId: txId, destTxHash: hex"01", relayer: relayerA}); + } + // ═══════════════════════════════════════════════════ CLAIM ═══════════════════════════════════════════════════════ function checkTokenBalancesAfterClaim(address relayer) public view { From abeea4db69107a6f12bfd7264a3bf49f5766860e Mon Sep 17 00:00:00 2001 From: parodime Date: Thu, 26 Sep 2024 11:23:34 +0000 Subject: [PATCH 37/90] Publish - @synapsecns/contracts-rfq@0.5.4 --- packages/contracts-rfq/CHANGELOG.md | 8 ++++++++ packages/contracts-rfq/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/contracts-rfq/CHANGELOG.md b/packages/contracts-rfq/CHANGELOG.md index 6b91e97f87..3743ed0540 100644 --- a/packages/contracts-rfq/CHANGELOG.md +++ b/packages/contracts-rfq/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.4](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.3...@synapsecns/contracts-rfq@0.5.4) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/contracts-rfq + + + + + ## [0.5.3](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.2...@synapsecns/contracts-rfq@0.5.3) (2024-09-26) **Note:** Version bump only for package @synapsecns/contracts-rfq diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index 979c16adad..76f4073981 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/contracts-rfq", "license": "MIT", - "version": "0.5.3", + "version": "0.5.4", "description": "FastBridge contracts.", "private": true, "files": [ From fc6ddaedf03f7769dab362f0bcdf81a3dd010516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CF=87=C2=B2?= <88190723+ChiTimesChi@users.noreply.github.com> Date: Thu, 26 Sep 2024 15:58:32 +0100 Subject: [PATCH 38/90] fix(sdk-router): disable ARB airdrop tests (#3195) --- packages/sdk-router/src/sdk.test.ts | 30 ++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/sdk-router/src/sdk.test.ts b/packages/sdk-router/src/sdk.test.ts index 4d49d9da45..fd79df50d2 100644 --- a/packages/sdk-router/src/sdk.test.ts +++ b/packages/sdk-router/src/sdk.test.ts @@ -54,7 +54,8 @@ global.fetch = jest.fn(() => const EXPECTED_GAS_DROP: { [chainId: number]: BigNumber } = { [SupportedChainId.ETH]: BigNumber.from(0), - [SupportedChainId.ARBITRUM]: parseFixed('0.0003', 18), + // TODO: reenable once both ARB airdrops are adjusted + // [SupportedChainId.ARBITRUM]: parseFixed('0.0003', 18), [SupportedChainId.BSC]: parseFixed('0.002', 18), [SupportedChainId.AVALANCHE]: parseFixed('0.025', 18), } @@ -296,9 +297,10 @@ describe('SynapseSDK', () => { MEDIAN_TIME_BRIDGE[SupportedChainId.ETH] ) expect(result.bridgeModuleName).toEqual('SynapseBridge') - expect(result.gasDropAmount).toEqual( - EXPECTED_GAS_DROP[SupportedChainId.ARBITRUM] - ) + // TODO: reenable + // expect(result.gasDropAmount).toEqual( + // EXPECTED_GAS_DROP[SupportedChainId.ARBITRUM] + // ) expect(result.originChainId).toEqual(SupportedChainId.ETH) expect(result.destChainId).toEqual(SupportedChainId.ARBITRUM) }) @@ -814,12 +816,13 @@ describe('SynapseSDK', () => { allQuotes[0].bridgeModuleName === 'SynapseCCTP' || allQuotes[1].bridgeModuleName === 'SynapseCCTP' ).toBe(true) - expect(allQuotes[0].gasDropAmount).toEqual( - EXPECTED_GAS_DROP[SupportedChainId.ARBITRUM] - ) - expect(allQuotes[1].gasDropAmount).toEqual( - EXPECTED_GAS_DROP[SupportedChainId.ARBITRUM] - ) + // TODO: reenable + // expect(allQuotes[0].gasDropAmount).toEqual( + // EXPECTED_GAS_DROP[SupportedChainId.ARBITRUM] + // ) + // expect(allQuotes[1].gasDropAmount).toEqual( + // EXPECTED_GAS_DROP[SupportedChainId.ARBITRUM] + // ) expect(allQuotes[0].originChainId).toEqual(SupportedChainId.ETH) expect(allQuotes[0].destChainId).toEqual(SupportedChainId.ARBITRUM) expect(allQuotes[1].originChainId).toEqual(SupportedChainId.ETH) @@ -848,9 +851,10 @@ describe('SynapseSDK', () => { expect(allQuotes.length).toEqual(1) expectCorrectBridgeQuote(allQuotes[0]) expect(allQuotes[0].bridgeModuleName).toEqual('SynapseBridge') - expect(allQuotes[0].gasDropAmount).toEqual( - EXPECTED_GAS_DROP[SupportedChainId.ARBITRUM] - ) + // TODO: reenable + // expect(allQuotes[0].gasDropAmount).toEqual( + // EXPECTED_GAS_DROP[SupportedChainId.ARBITRUM] + // ) }) }) From 90ac800000228d7bef0700c460f3dddf59349e27 Mon Sep 17 00:00:00 2001 From: ChiTimesChi Date: Thu, 26 Sep 2024 15:02:57 +0000 Subject: [PATCH 39/90] Publish - @synapsecns/rest-api@1.3.1 - @synapsecns/sdk-router@0.11.2 - @synapsecns/synapse-interface@0.39.1 - @synapsecns/widget@0.7.2 --- packages/rest-api/CHANGELOG.md | 8 ++++++++ packages/rest-api/package.json | 4 ++-- packages/sdk-router/CHANGELOG.md | 11 +++++++++++ packages/sdk-router/package.json | 2 +- packages/synapse-interface/CHANGELOG.md | 8 ++++++++ packages/synapse-interface/package.json | 4 ++-- packages/widget/CHANGELOG.md | 8 ++++++++ packages/widget/package.json | 4 ++-- 8 files changed, 42 insertions(+), 7 deletions(-) diff --git a/packages/rest-api/CHANGELOG.md b/packages/rest-api/CHANGELOG.md index b56508ac64..8cc0cf3007 100644 --- a/packages/rest-api/CHANGELOG.md +++ b/packages/rest-api/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.3.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.3.0...@synapsecns/rest-api@1.3.1) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/rest-api + + + + + # [1.3.0](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.2.0...@synapsecns/rest-api@1.3.0) (2024-09-25) diff --git a/packages/rest-api/package.json b/packages/rest-api/package.json index 7f52b3613e..668a54a6df 100644 --- a/packages/rest-api/package.json +++ b/packages/rest-api/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/rest-api", - "version": "1.3.0", + "version": "1.3.1", "private": "true", "engines": { "node": ">=18.17.0" @@ -22,7 +22,7 @@ "@ethersproject/bignumber": "^5.7.0", "@ethersproject/providers": "^5.7.2", "@ethersproject/units": "5.7.0", - "@synapsecns/sdk-router": "^0.11.1", + "@synapsecns/sdk-router": "^0.11.2", "bignumber": "^1.1.0", "ethers": "5.7.2", "express": "^4.18.2", diff --git a/packages/sdk-router/CHANGELOG.md b/packages/sdk-router/CHANGELOG.md index f66f3b5ab4..fff8579edf 100644 --- a/packages/sdk-router/CHANGELOG.md +++ b/packages/sdk-router/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.11.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/sdk-router@0.11.1...@synapsecns/sdk-router@0.11.2) (2024-09-26) + + +### Bug Fixes + +* **sdk-router:** disable ARB airdrop tests ([#3195](https://github.com/synapsecns/sanguine/issues/3195)) ([fc6ddae](https://github.com/synapsecns/sanguine/commit/fc6ddaedf03f7769dab362f0bcdf81a3dd010516)) + + + + + ## [0.11.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/sdk-router@0.11.0...@synapsecns/sdk-router@0.11.1) (2024-09-04) **Note:** Version bump only for package @synapsecns/sdk-router diff --git a/packages/sdk-router/package.json b/packages/sdk-router/package.json index 56f4c91bea..6042ce0125 100644 --- a/packages/sdk-router/package.json +++ b/packages/sdk-router/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/sdk-router", "description": "An SDK for interacting with the Synapse Protocol", - "version": "0.11.1", + "version": "0.11.2", "license": "MIT", "main": "dist/index.js", "typings": "dist/index.d.ts", diff --git a/packages/synapse-interface/CHANGELOG.md b/packages/synapse-interface/CHANGELOG.md index bdaf008aa9..516afa1d79 100644 --- a/packages/synapse-interface/CHANGELOG.md +++ b/packages/synapse-interface/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.39.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.39.0...@synapsecns/synapse-interface@0.39.1) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/synapse-interface + + + + + # [0.39.0](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.38.9...@synapsecns/synapse-interface@0.39.0) (2024-09-23) diff --git a/packages/synapse-interface/package.json b/packages/synapse-interface/package.json index 97217d87a4..1424a86ea4 100644 --- a/packages/synapse-interface/package.json +++ b/packages/synapse-interface/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/synapse-interface", - "version": "0.39.0", + "version": "0.39.1", "private": true, "engines": { "node": ">=18.18.0" @@ -37,7 +37,7 @@ "@reduxjs/toolkit": "^1.9.5", "@rtk-query/graphql-request-base-query": "^2.2.0", "@segment/analytics-next": "^1.53.0", - "@synapsecns/sdk-router": "^0.11.1", + "@synapsecns/sdk-router": "^0.11.2", "@tailwindcss/aspect-ratio": "^0.4.2", "@tailwindcss/forms": "^0.5.3", "@tailwindcss/typography": "^0.5.9", diff --git a/packages/widget/CHANGELOG.md b/packages/widget/CHANGELOG.md index f4b030108b..5bfd1dc8af 100644 --- a/packages/widget/CHANGELOG.md +++ b/packages/widget/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.7.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/widget@0.7.1...@synapsecns/widget@0.7.2) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/widget + + + + + ## [0.7.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/widget@0.7.0...@synapsecns/widget@0.7.1) (2024-09-04) **Note:** Version bump only for package @synapsecns/widget diff --git a/packages/widget/package.json b/packages/widget/package.json index eafb1d4c20..bff3072665 100644 --- a/packages/widget/package.json +++ b/packages/widget/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/widget", "description": "Widget library for interacting with the Synapse Protocol", - "version": "0.7.1", + "version": "0.7.2", "license": "MIT", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", @@ -68,7 +68,7 @@ "@ethersproject/providers": "^5.7.2", "@ethersproject/units": "^5.7.0", "@reduxjs/toolkit": "^2.0.1", - "@synapsecns/sdk-router": "^0.11.1", + "@synapsecns/sdk-router": "^0.11.2", "ethers": "^6.9.1", "lodash": "^4.17.21", "react-redux": "^9.0.2" From fe71628bb25a406afe6b9ac5051231a4b9416865 Mon Sep 17 00:00:00 2001 From: Moses <103143573+Defi-Moses@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:49:17 +0100 Subject: [PATCH 40/90] Fixing issue for wallet integration [SLT-270] (#3194) * slight modification to graphql call * fixing explorer frontend as well --- packages/explorer-ui/pages/tx/[kappa].tsx | 12 +- .../controllers/destinationTxController.ts | 8 +- yarn.lock | 643 +++++++++++++++++- 3 files changed, 645 insertions(+), 18 deletions(-) diff --git a/packages/explorer-ui/pages/tx/[kappa].tsx b/packages/explorer-ui/pages/tx/[kappa].tsx index 05ac17ca7e..75bfb80b9a 100644 --- a/packages/explorer-ui/pages/tx/[kappa].tsx +++ b/packages/explorer-ui/pages/tx/[kappa].tsx @@ -10,7 +10,7 @@ import { API_URL } from '@graphql' import { HorizontalDivider } from '@components/misc/HorizontalDivider' import { formatDateTimestamp } from '@utils/formatDate' import { IconAndAmount } from '@components/misc/IconAndAmount' - +import { addressToSymbol } from '@utils/addressToSymbol' const CHAINS_BY_ID = CHAINS.CHAINS_BY_ID const link = new HttpLink({ @@ -157,7 +157,10 @@ export const BridgeTransaction = ({ queryResult }) => { value={fromInfo.value} tokenAddress={fromInfo.tokenAddress} chainId={fromInfo.chainID} - tokenSymbol={fromInfo.tokenSymbol} + tokenSymbol={addressToSymbol({ + tokenAddress: fromInfo.tokenAddress, + chainId: fromInfo.chainID, + })} iconSize="w-4 h-4" // textSize="text-sm" // styledCoin={true} @@ -183,7 +186,10 @@ export const BridgeTransaction = ({ queryResult }) => { value={toInfo.value} tokenAddress={toInfo.tokenAddress} chainId={toInfo.chainID} - tokenSymbol={toInfo.tokenSymbol} + tokenSymbol={addressToSymbol({ + tokenAddress: toInfo.tokenAddress, + chainId: toInfo.chainID, + })} iconSize="w-4 h-4" // textSize="text-sm" // styledCoin={true} diff --git a/packages/rest-api/src/controllers/destinationTxController.ts b/packages/rest-api/src/controllers/destinationTxController.ts index 8db51772a2..bedc02c7f6 100644 --- a/packages/rest-api/src/controllers/destinationTxController.ts +++ b/packages/rest-api/src/controllers/destinationTxController.ts @@ -2,6 +2,7 @@ import { validationResult } from 'express-validator' import { ethers } from 'ethers' import { getTokenDecimals } from '../utils/getTokenDecimals' +import { tokenAddressToToken } from '../utils/tokenAddressToToken' export const destinationTxController = async (req, res) => { const errors = validationResult(req) @@ -47,15 +48,18 @@ export const destinationTxController = async (req, res) => { const toInfo = graphqlData.data.bridgeTransactions[0]?.toInfo || null if (toInfo) { - const { tokenAddress, value, ...restToInfo } = toInfo + const { tokenAddress, value, chainID, ...restToInfo } = toInfo - const tokenDecimals = getTokenDecimals(toInfo.chainID, tokenAddress) + const tokenInfo = tokenAddressToToken(chainID.toString(), tokenAddress) + const tokenDecimals = getTokenDecimals(chainID, tokenAddress) const formattedValue = ethers.utils.formatUnits(value, tokenDecimals) res.json({ status: 'completed', toInfo: { + chainID, ...restToInfo, + tokenSymbol: tokenInfo ? tokenInfo?.symbol : null, formattedValue: `${formattedValue}`, }, }) diff --git a/yarn.lock b/yarn.lock index 826b7331b8..65b9c966b1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1523,7 +1523,7 @@ "@babel/parser" "^7.25.0" "@babel/types" "^7.25.0" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.11.5", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.22.8", "@babel/traverse@^7.23.2", "@babel/traverse@^7.24.7", "@babel/traverse@^7.24.8", "@babel/traverse@^7.25.0", "@babel/traverse@^7.25.1", "@babel/traverse@^7.25.2", "@babel/traverse@^7.25.3", "@babel/traverse@^7.25.4", "@babel/traverse@^7.7.0": +"@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.11.5", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.22.8", "@babel/traverse@^7.23.2", "@babel/traverse@^7.24.7", "@babel/traverse@^7.24.8", "@babel/traverse@^7.25.0", "@babel/traverse@^7.25.1", "@babel/traverse@^7.25.2", "@babel/traverse@^7.25.3", "@babel/traverse@^7.25.4", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2": version "7.25.6" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.6.tgz#04fad980e444f182ecf1520504941940a90fea41" integrity sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ== @@ -4383,6 +4383,18 @@ jest-util "^25.5.0" slash "^3.0.0" +"@jest/console@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-28.1.3.tgz#2030606ec03a18c31803b8a36382762e447655df" + integrity sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw== + dependencies: + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^28.1.3" + jest-util "^28.1.3" + slash "^3.0.0" + "@jest/console@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" @@ -4429,6 +4441,41 @@ slash "^3.0.0" strip-ansi "^6.0.0" +"@jest/core@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-28.1.3.tgz#0ebf2bd39840f1233cd5f2d1e6fc8b71bd5a1ac7" + integrity sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA== + dependencies: + "@jest/console" "^28.1.3" + "@jest/reporters" "^28.1.3" + "@jest/test-result" "^28.1.3" + "@jest/transform" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + ci-info "^3.2.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^28.1.3" + jest-config "^28.1.3" + jest-haste-map "^28.1.3" + jest-message-util "^28.1.3" + jest-regex-util "^28.0.2" + jest-resolve "^28.1.3" + jest-resolve-dependencies "^28.1.3" + jest-runner "^28.1.3" + jest-runtime "^28.1.3" + jest-snapshot "^28.1.3" + jest-util "^28.1.3" + jest-validate "^28.1.3" + jest-watcher "^28.1.3" + micromatch "^4.0.4" + pretty-format "^28.1.3" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + "@jest/core@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" @@ -4472,6 +4519,16 @@ "@jest/types" "^25.5.0" jest-mock "^25.5.0" +"@jest/environment@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-28.1.3.tgz#abed43a6b040a4c24fdcb69eab1f97589b2d663e" + integrity sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA== + dependencies: + "@jest/fake-timers" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + jest-mock "^28.1.3" + "@jest/environment@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" @@ -4482,6 +4539,13 @@ "@types/node" "*" jest-mock "^29.7.0" +"@jest/expect-utils@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" + integrity sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA== + dependencies: + jest-get-type "^28.0.2" + "@jest/expect-utils@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" @@ -4489,6 +4553,14 @@ dependencies: jest-get-type "^29.6.3" +"@jest/expect@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-28.1.3.tgz#9ac57e1d4491baca550f6bdbd232487177ad6a72" + integrity sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw== + dependencies: + expect "^28.1.3" + jest-snapshot "^28.1.3" + "@jest/expect@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" @@ -4508,6 +4580,18 @@ jest-util "^25.5.0" lolex "^5.0.0" +"@jest/fake-timers@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-28.1.3.tgz#230255b3ad0a3d4978f1d06f70685baea91c640e" + integrity sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw== + dependencies: + "@jest/types" "^28.1.3" + "@sinonjs/fake-timers" "^9.1.2" + "@types/node" "*" + jest-message-util "^28.1.3" + jest-mock "^28.1.3" + jest-util "^28.1.3" + "@jest/fake-timers@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" @@ -4529,6 +4613,15 @@ "@jest/types" "^25.5.0" expect "^25.5.0" +"@jest/globals@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-28.1.3.tgz#a601d78ddc5fdef542728309894895b4a42dc333" + integrity sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA== + dependencies: + "@jest/environment" "^28.1.3" + "@jest/expect" "^28.1.3" + "@jest/types" "^28.1.3" + "@jest/globals@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" @@ -4571,6 +4664,37 @@ optionalDependencies: node-notifier "^6.0.0" +"@jest/reporters@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-28.1.3.tgz#9adf6d265edafc5fc4a434cfb31e2df5a67a369a" + integrity sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^28.1.3" + "@jest/test-result" "^28.1.3" + "@jest/transform" "^28.1.3" + "@jest/types" "^28.1.3" + "@jridgewell/trace-mapping" "^0.3.13" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^5.1.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-message-util "^28.1.3" + jest-util "^28.1.3" + jest-worker "^28.1.3" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + terminal-link "^2.0.0" + v8-to-istanbul "^9.0.1" + "@jest/reporters@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" @@ -4601,6 +4725,13 @@ strip-ansi "^6.0.0" v8-to-istanbul "^9.0.1" +"@jest/schemas@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" + integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg== + dependencies: + "@sinclair/typebox" "^0.24.1" + "@jest/schemas@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" @@ -4617,6 +4748,15 @@ graceful-fs "^4.2.4" source-map "^0.6.0" +"@jest/source-map@^28.1.2": + version "28.1.2" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-28.1.2.tgz#7fe832b172b497d6663cdff6c13b0a920e139e24" + integrity sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww== + dependencies: + "@jridgewell/trace-mapping" "^0.3.13" + callsites "^3.0.0" + graceful-fs "^4.2.9" + "@jest/source-map@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" @@ -4636,6 +4776,16 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" +"@jest/test-result@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.3.tgz#5eae945fd9f4b8fcfce74d239e6f725b6bf076c5" + integrity sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg== + dependencies: + "@jest/console" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + "@jest/test-result@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" @@ -4657,6 +4807,16 @@ jest-runner "^25.5.4" jest-runtime "^25.5.4" +"@jest/test-sequencer@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz#9d0c283d906ac599c74bde464bc0d7e6a82886c3" + integrity sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw== + dependencies: + "@jest/test-result" "^28.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^28.1.3" + slash "^3.0.0" + "@jest/test-sequencer@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" @@ -4710,6 +4870,27 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" +"@jest/transform@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-28.1.3.tgz#59d8098e50ab07950e0f2fc0fc7ec462371281b0" + integrity sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^28.1.3" + "@jridgewell/trace-mapping" "^0.3.13" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^28.1.3" + jest-regex-util "^28.0.2" + jest-util "^28.1.3" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.1" + "@jest/transform@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" @@ -4772,6 +4953,18 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@jest/types@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b" + integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ== + dependencies: + "@jest/schemas" "^28.1.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + "@jest/types@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" @@ -4824,7 +5017,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== @@ -7314,6 +7507,11 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== +"@sinclair/typebox@^0.24.1": + version "0.24.51" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" + integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== + "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -7360,6 +7558,13 @@ dependencies: "@sinonjs/commons" "^3.0.0" +"@sinonjs/fake-timers@^9.1.2": + version "9.1.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" + integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@slorber/remark-comment@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@slorber/remark-comment/-/remark-comment-1.0.0.tgz#2a020b3f4579c89dec0361673206c28d67e08f5a" @@ -8689,14 +8894,6 @@ "@swc/counter" "^0.1.3" tslib "^2.4.0" -"@synapsecns/coverage-aggregator@file:./packages/coverage-aggregator": - version "1.0.6" - dependencies: - glob "^8.0.3" - path "^0.12.7" - ts-jest "^29.0.5" - yargs "^17.6.2" - "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -9581,7 +9778,7 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== -"@types/prettier@^2.1.1": +"@types/prettier@^2.1.1", "@types/prettier@^2.1.5": version "2.7.3" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== @@ -12171,6 +12368,19 @@ babel-jest@^25.2.6, babel-jest@^25.5.1: graceful-fs "^4.2.4" slash "^3.0.0" +babel-jest@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-28.1.3.tgz#c1187258197c099072156a0a121c11ee1e3917d5" + integrity sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q== + dependencies: + "@jest/transform" "^28.1.3" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^28.1.3" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + babel-jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" @@ -12275,6 +12485,16 @@ babel-plugin-jest-hoist@^25.5.0: "@babel/types" "^7.3.3" "@types/babel__traverse" "^7.0.6" +babel-plugin-jest-hoist@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz#1952c4d0ea50f2d6d794353762278d1d8cca3fbe" + integrity sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + babel-plugin-jest-hoist@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" @@ -12470,6 +12690,14 @@ babel-preset-jest@^25.5.0: babel-plugin-jest-hoist "^25.5.0" babel-preset-current-node-syntax "^0.1.2" +babel-preset-jest@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz#5dfc20b99abed5db994406c2b9ab94c73aaa419d" + integrity sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A== + dependencies: + babel-plugin-jest-hoist "^28.1.3" + babel-preset-current-node-syntax "^1.0.0" + babel-preset-jest@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" @@ -15946,6 +16174,11 @@ diff-sequences@^25.2.6: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg== +diff-sequences@^28.1.1: + version "28.1.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" + integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw== + diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" @@ -16434,6 +16667,11 @@ elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4, elliptic@^6. minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +emittery@^0.10.2: + version "0.10.2" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" + integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== + emittery@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" @@ -18259,6 +18497,17 @@ expect@^25.5.0: jest-message-util "^25.5.0" jest-regex-util "^25.2.6" +expect@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec" + integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g== + dependencies: + "@jest/expect-utils" "^28.1.3" + jest-get-type "^28.0.2" + jest-matcher-utils "^28.1.3" + jest-message-util "^28.1.3" + jest-util "^28.1.3" + expect@^29.0.0, expect@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" @@ -22214,7 +22463,7 @@ istanbul-lib-instrument@^4.0.0: istanbul-lib-coverage "^3.0.0" semver "^6.3.0" -istanbul-lib-instrument@^5.0.4: +istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== @@ -22334,6 +22583,14 @@ jest-changed-files@^25.5.0: execa "^3.2.0" throat "^5.0.0" +jest-changed-files@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-28.1.3.tgz#d9aeee6792be3686c47cb988a8eaf82ff4238831" + integrity sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA== + dependencies: + execa "^5.0.0" + p-limit "^3.1.0" + jest-changed-files@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" @@ -22343,6 +22600,31 @@ jest-changed-files@^29.7.0: jest-util "^29.7.0" p-limit "^3.1.0" +jest-circus@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-28.1.3.tgz#d14bd11cf8ee1a03d69902dc47b6bd4634ee00e4" + integrity sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow== + dependencies: + "@jest/environment" "^28.1.3" + "@jest/expect" "^28.1.3" + "@jest/test-result" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + is-generator-fn "^2.0.0" + jest-each "^28.1.3" + jest-matcher-utils "^28.1.3" + jest-message-util "^28.1.3" + jest-runtime "^28.1.3" + jest-snapshot "^28.1.3" + jest-util "^28.1.3" + p-limit "^3.1.0" + pretty-format "^28.1.3" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-circus@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" @@ -22389,6 +22671,24 @@ jest-cli@^25.5.4: realpath-native "^2.0.0" yargs "^15.3.1" +jest-cli@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-28.1.3.tgz#558b33c577d06de55087b8448d373b9f654e46b2" + integrity sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ== + dependencies: + "@jest/core" "^28.1.3" + "@jest/test-result" "^28.1.3" + "@jest/types" "^28.1.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + import-local "^3.0.2" + jest-config "^28.1.3" + jest-util "^28.1.3" + jest-validate "^28.1.3" + prompts "^2.0.1" + yargs "^17.3.1" + jest-cli@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" @@ -22431,6 +22731,34 @@ jest-config@^25.5.4: pretty-format "^25.5.0" realpath-native "^2.0.0" +jest-config@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-28.1.3.tgz#e315e1f73df3cac31447eed8b8740a477392ec60" + integrity sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^28.1.3" + "@jest/types" "^28.1.3" + babel-jest "^28.1.3" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^28.1.3" + jest-environment-node "^28.1.3" + jest-get-type "^28.0.2" + jest-regex-util "^28.0.2" + jest-resolve "^28.1.3" + jest-runner "^28.1.3" + jest-util "^28.1.3" + jest-validate "^28.1.3" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^28.1.3" + slash "^3.0.0" + strip-json-comments "^3.1.1" + jest-config@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" @@ -22479,6 +22807,16 @@ jest-diff@^25.2.1, jest-diff@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" +jest-diff@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f" + integrity sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw== + dependencies: + chalk "^4.0.0" + diff-sequences "^28.1.1" + jest-get-type "^28.0.2" + pretty-format "^28.1.3" + jest-diff@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" @@ -22496,6 +22834,13 @@ jest-docblock@^25.3.0: dependencies: detect-newline "^3.0.0" +jest-docblock@^28.1.1: + version "28.1.1" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-28.1.1.tgz#6f515c3bf841516d82ecd57a62eed9204c2f42a8" + integrity sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA== + dependencies: + detect-newline "^3.0.0" + jest-docblock@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" @@ -22514,6 +22859,17 @@ jest-each@^25.5.0: jest-util "^25.5.0" pretty-format "^25.5.0" +jest-each@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-28.1.3.tgz#bdd1516edbe2b1f3569cfdad9acd543040028f81" + integrity sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g== + dependencies: + "@jest/types" "^28.1.3" + chalk "^4.0.0" + jest-get-type "^28.0.2" + jest-util "^28.1.3" + pretty-format "^28.1.3" + jest-each@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" @@ -22549,6 +22905,18 @@ jest-environment-node@^25.5.0: jest-util "^25.5.0" semver "^6.3.0" +jest-environment-node@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-28.1.3.tgz#7e74fe40eb645b9d56c0c4b70ca4357faa349be5" + integrity sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A== + dependencies: + "@jest/environment" "^28.1.3" + "@jest/fake-timers" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + jest-mock "^28.1.3" + jest-util "^28.1.3" + jest-environment-node@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" @@ -22579,6 +22947,11 @@ jest-get-type@^25.2.6: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig== +jest-get-type@^28.0.2: + version "28.0.2" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" + integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== + jest-get-type@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" @@ -22625,6 +22998,25 @@ jest-haste-map@^26.6.2: optionalDependencies: fsevents "^2.1.2" +jest-haste-map@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-28.1.3.tgz#abd5451129a38d9841049644f34b034308944e2b" + integrity sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA== + dependencies: + "@jest/types" "^28.1.3" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^28.0.2" + jest-util "^28.1.3" + jest-worker "^28.1.3" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + jest-haste-map@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" @@ -22675,6 +23067,14 @@ jest-leak-detector@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" +jest-leak-detector@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz#a6685d9b074be99e3adee816ce84fd30795e654d" + integrity sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA== + dependencies: + jest-get-type "^28.0.2" + pretty-format "^28.1.3" + jest-leak-detector@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" @@ -22693,6 +23093,16 @@ jest-matcher-utils@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" +jest-matcher-utils@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e" + integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw== + dependencies: + chalk "^4.0.0" + jest-diff "^28.1.3" + jest-get-type "^28.0.2" + pretty-format "^28.1.3" + jest-matcher-utils@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" @@ -22717,6 +23127,21 @@ jest-message-util@^25.5.0: slash "^3.0.0" stack-utils "^1.0.1" +jest-message-util@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d" + integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^28.1.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^28.1.3" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-message-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" @@ -22754,6 +23179,14 @@ jest-mock@^27.0.6: "@jest/types" "^27.5.1" "@types/node" "*" +jest-mock@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-28.1.3.tgz#d4e9b1fc838bea595c77ab73672ebf513ab249da" + integrity sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA== + dependencies: + "@jest/types" "^28.1.3" + "@types/node" "*" + jest-mock@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" @@ -22778,6 +23211,11 @@ jest-regex-util@^26.0.0: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== +jest-regex-util@^28.0.2: + version "28.0.2" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead" + integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw== + jest-regex-util@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" @@ -22792,6 +23230,14 @@ jest-resolve-dependencies@^25.5.4: jest-regex-util "^25.2.6" jest-snapshot "^25.5.1" +jest-resolve-dependencies@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz#8c65d7583460df7275c6ea2791901fa975c1fe66" + integrity sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA== + dependencies: + jest-regex-util "^28.0.2" + jest-snapshot "^28.1.3" + jest-resolve-dependencies@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" @@ -22815,6 +23261,21 @@ jest-resolve@^25.5.1: resolve "^1.17.0" slash "^3.0.0" +jest-resolve@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-28.1.3.tgz#cfb36100341ddbb061ec781426b3c31eb51aa0a8" + integrity sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^28.1.3" + jest-pnp-resolver "^1.2.2" + jest-util "^28.1.3" + jest-validate "^28.1.3" + resolve "^1.20.0" + resolve.exports "^1.1.0" + slash "^3.0.0" + jest-resolve@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" @@ -22855,6 +23316,33 @@ jest-runner@^25.5.4: source-map-support "^0.5.6" throat "^5.0.0" +jest-runner@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-28.1.3.tgz#5eee25febd730b4713a2cdfd76bdd5557840f9a1" + integrity sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA== + dependencies: + "@jest/console" "^28.1.3" + "@jest/environment" "^28.1.3" + "@jest/test-result" "^28.1.3" + "@jest/transform" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.10.2" + graceful-fs "^4.2.9" + jest-docblock "^28.1.1" + jest-environment-node "^28.1.3" + jest-haste-map "^28.1.3" + jest-leak-detector "^28.1.3" + jest-message-util "^28.1.3" + jest-resolve "^28.1.3" + jest-runtime "^28.1.3" + jest-util "^28.1.3" + jest-watcher "^28.1.3" + jest-worker "^28.1.3" + p-limit "^3.1.0" + source-map-support "0.5.13" + jest-runner@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" @@ -22914,6 +23402,34 @@ jest-runtime@^25.5.4: strip-bom "^4.0.0" yargs "^15.3.1" +jest-runtime@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-28.1.3.tgz#a57643458235aa53e8ec7821949e728960d0605f" + integrity sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw== + dependencies: + "@jest/environment" "^28.1.3" + "@jest/fake-timers" "^28.1.3" + "@jest/globals" "^28.1.3" + "@jest/source-map" "^28.1.2" + "@jest/test-result" "^28.1.3" + "@jest/transform" "^28.1.3" + "@jest/types" "^28.1.3" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + execa "^5.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^28.1.3" + jest-message-util "^28.1.3" + jest-mock "^28.1.3" + jest-regex-util "^28.0.2" + jest-resolve "^28.1.3" + jest-snapshot "^28.1.3" + jest-util "^28.1.3" + slash "^3.0.0" + strip-bom "^4.0.0" + jest-runtime@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" @@ -22978,6 +23494,35 @@ jest-snapshot@^25.5.1: pretty-format "^25.5.0" semver "^6.3.0" +jest-snapshot@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-28.1.3.tgz#17467b3ab8ddb81e2f605db05583d69388fc0668" + integrity sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^28.1.3" + "@jest/transform" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/babel__traverse" "^7.0.6" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^28.1.3" + graceful-fs "^4.2.9" + jest-diff "^28.1.3" + jest-get-type "^28.0.2" + jest-haste-map "^28.1.3" + jest-matcher-utils "^28.1.3" + jest-message-util "^28.1.3" + jest-util "^28.1.3" + natural-compare "^1.4.0" + pretty-format "^28.1.3" + semver "^7.3.5" + jest-snapshot@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" @@ -23027,6 +23572,18 @@ jest-util@^26.6.2: is-ci "^2.0.0" micromatch "^4.0.2" +jest-util@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" + integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ== + dependencies: + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-util@^29.0.0, jest-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" @@ -23051,6 +23608,18 @@ jest-validate@^25.5.0: leven "^3.1.0" pretty-format "^25.5.0" +jest-validate@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-28.1.3.tgz#e322267fd5e7c64cea4629612c357bbda96229df" + integrity sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA== + dependencies: + "@jest/types" "^28.1.3" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^28.0.2" + leven "^3.1.0" + pretty-format "^28.1.3" + jest-validate@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" @@ -23088,6 +23657,20 @@ jest-watcher@^25.2.4, jest-watcher@^25.5.0: jest-util "^25.5.0" string-length "^3.1.0" +jest-watcher@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-28.1.3.tgz#c6023a59ba2255e3b4c57179fc94164b3e73abd4" + integrity sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g== + dependencies: + "@jest/test-result" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.10.2" + jest-util "^28.1.3" + string-length "^4.0.1" + jest-watcher@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" @@ -23136,6 +23719,15 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" +jest-worker@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.3.tgz#7e3c4ce3fa23d1bb6accb169e7f396f98ed4bb98" + integrity sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + jest-worker@^29.1.2, jest-worker@^29.4.3, jest-worker@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" @@ -23155,6 +23747,16 @@ jest@^25.3.0: import-local "^3.0.2" jest-cli "^25.5.4" +jest@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest/-/jest-28.1.3.tgz#e9c6a7eecdebe3548ca2b18894a50f45b36dfc6b" + integrity sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA== + dependencies: + "@jest/core" "^28.1.3" + "@jest/types" "^28.1.3" + import-local "^3.0.2" + jest-cli "^28.1.3" + jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" @@ -29346,6 +29948,16 @@ pretty-format@^27.0.2: ansi-styles "^5.0.0" react-is "^17.0.1" +pretty-format@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" + integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q== + dependencies: + "@jest/schemas" "^28.1.3" + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^18.0.0" + pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" @@ -31134,6 +31746,11 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== +resolve.exports@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" + integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== + resolve.exports@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" @@ -37376,7 +37993,7 @@ write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write-file-atomic@^4.0.2: +write-file-atomic@^4.0.1, write-file-atomic@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== From 5c8a06944c144ead7be962095c1741f79eec966f Mon Sep 17 00:00:00 2001 From: Defi-Moses Date: Thu, 26 Sep 2024 15:53:26 +0000 Subject: [PATCH 41/90] Publish - @synapsecns/explorer-ui@0.3.3 - @synapsecns/rest-api@1.3.2 --- packages/explorer-ui/CHANGELOG.md | 8 ++++++++ packages/explorer-ui/package.json | 2 +- packages/rest-api/CHANGELOG.md | 8 ++++++++ packages/rest-api/package.json | 2 +- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/explorer-ui/CHANGELOG.md b/packages/explorer-ui/CHANGELOG.md index 73cdb24b88..3367d054fe 100644 --- a/packages/explorer-ui/CHANGELOG.md +++ b/packages/explorer-ui/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.3](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.2...@synapsecns/explorer-ui@0.3.3) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/explorer-ui + + + + + ## [0.3.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.1...@synapsecns/explorer-ui@0.3.2) (2024-09-10) **Note:** Version bump only for package @synapsecns/explorer-ui diff --git a/packages/explorer-ui/package.json b/packages/explorer-ui/package.json index 8e06fd66dc..980a2fd805 100644 --- a/packages/explorer-ui/package.json +++ b/packages/explorer-ui/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/explorer-ui", - "version": "0.3.2", + "version": "0.3.3", "private": true, "engines": { "node": ">=18.17.0" diff --git a/packages/rest-api/CHANGELOG.md b/packages/rest-api/CHANGELOG.md index 8cc0cf3007..fab320e869 100644 --- a/packages/rest-api/CHANGELOG.md +++ b/packages/rest-api/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.3.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.3.1...@synapsecns/rest-api@1.3.2) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/rest-api + + + + + ## [1.3.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.3.0...@synapsecns/rest-api@1.3.1) (2024-09-26) **Note:** Version bump only for package @synapsecns/rest-api diff --git a/packages/rest-api/package.json b/packages/rest-api/package.json index 668a54a6df..5b6c7f8c1b 100644 --- a/packages/rest-api/package.json +++ b/packages/rest-api/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/rest-api", - "version": "1.3.1", + "version": "1.3.2", "private": "true", "engines": { "node": ">=18.17.0" From 65ee4961d0d93b8633cfce7b67dcf20c93c6e661 Mon Sep 17 00:00:00 2001 From: parodime Date: Thu, 26 Sep 2024 12:02:28 -0400 Subject: [PATCH 42/90] store relayer on relay [SLT-182] (#3170) * store relayer on relay [SLT-182] * +tests, zeroAddr check, fmt --- .../contracts-rfq/contracts/FastBridgeV2.sol | 17 ++++++++++---- .../contracts/interfaces/IFastBridgeV2.sol | 10 +++++++++ .../contracts-rfq/test/FastBridgeV2.Dst.t.sol | 22 +++++++++++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/packages/contracts-rfq/contracts/FastBridgeV2.sol b/packages/contracts-rfq/contracts/FastBridgeV2.sol index 99c3568de1..adf48dd1f3 100644 --- a/packages/contracts-rfq/contracts/FastBridgeV2.sol +++ b/packages/contracts-rfq/contracts/FastBridgeV2.sol @@ -36,8 +36,8 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { mapping(bytes32 => BridgeStatus) public bridgeStatuses; /// @notice Proof of relayed bridge tx on origin chain mapping(bytes32 => BridgeProof) public bridgeProofs; - /// @notice Whether bridge has been relayed on destination chain - mapping(bytes32 => bool) public bridgeRelays; + /// @notice Relay details on destination chain + mapping(bytes32 => BridgeRelay) public bridgeRelayDetails; /// @dev to prevent replays uint256 public nonce; @@ -132,6 +132,7 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { /// @inheritdoc IFastBridgeV2 function relay(bytes memory request, address relayer) public payable { + if (relayer == address(0)) revert ZeroAddress(); bytes32 transactionId = keccak256(request); BridgeTransaction memory transaction = getBridgeTransaction(request); if (transaction.destChainId != uint32(block.chainid)) revert ChainIncorrect(); @@ -139,9 +140,11 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { // check haven't exceeded deadline for relay to happen if (block.timestamp > transaction.deadline) revert DeadlineExceeded(); + if (bridgeRelayDetails[transactionId].relayer != address(0)) revert TransactionRelayed(); + // mark bridge transaction as relayed - if (bridgeRelays[transactionId]) revert TransactionRelayed(); - bridgeRelays[transactionId] = true; + bridgeRelayDetails[transactionId] = + BridgeRelay({blockNumber: uint48(block.number), blockTimestamp: uint48(block.timestamp), relayer: relayer}); // transfer tokens to recipient on destination chain and gas rebate if requested address to = transaction.destRecipient; @@ -175,6 +178,12 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { ); } + /// @inheritdoc IFastBridgeV2 + function bridgeRelays(bytes32 transactionId) public view returns (bool) { + // has this transactionId been relayed? + return bridgeRelayDetails[transactionId].relayer != address(0); + } + /// @inheritdoc IFastBridge function prove(bytes memory request, bytes32 destTxHash) external { bytes32 transactionId = keccak256(request); diff --git a/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol index 0d9cd33fcb..cec58048f8 100644 --- a/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol +++ b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol @@ -4,6 +4,12 @@ pragma solidity ^0.8.20; import {IFastBridge} from "./IFastBridge.sol"; interface IFastBridgeV2 is IFastBridge { + struct BridgeRelay { + uint48 blockNumber; + uint48 blockTimestamp; + address relayer; + } + /// @notice Relays destination side of bridge transaction by off-chain relayer /// @param request The encoded bridge transaction to relay on destination chain /// @param relayer The address of the relaying entity which should have control of the origin funds when claimed @@ -19,4 +25,8 @@ interface IFastBridgeV2 is IFastBridge { /// @notice Can only send funds to the relayer address on the proof. /// @param request The encoded bridge transaction to claim on origin chain function claim(bytes memory request) external; + /// @notice Checks if a transaction has been relayed + /// @param transactionId The ID of the transaction to check + /// @return True if the transaction has been relayed, false otherwise + function bridgeRelays(bytes32 transactionId) external view returns (bool); } diff --git a/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol index b9957b85ca..e4848d53dc 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; +import {ChainIncorrect, DeadlineExceeded, TransactionRelayed, ZeroAddress} from "../contracts/libs/Errors.sol"; + import {FastBridgeV2, FastBridgeV2Test, IFastBridge} from "./FastBridgeV2.t.sol"; // solhint-disable func-name-mixedcase, ordering @@ -113,6 +115,21 @@ contract FastBridgeV2DstTest is FastBridgeV2Test { assertEq(address(fastBridge).balance, 0); } + /// @notice RelayerB completes the ETH bridge request, using relayerA's address + function test_relay_eth_withRelayerAddress_checkBlockData() public { + vm.roll(987_654_321); + vm.warp(123_456_789); + bytes32 txId = getTxId(ethTx); + expectBridgeRelayed(ethTx, txId, address(relayerA)); + relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + assertTrue(fastBridge.bridgeRelays(txId)); + (uint48 recordedBlockNumber, uint48 recordedblockTimestamp,) = fastBridge.bridgeRelayDetails(txId); + assertEq(recordedBlockNumber, 987_654_321); + assertEq(recordedblockTimestamp, 123_456_789); + assertEq(address(userB).balance, ethParams.destAmount); + assertEq(address(relayerB).balance, LEFTOVER_BALANCE); + assertEq(address(fastBridge).balance, 0); + } // ══════════════════════════════════════════════════ REVERTS ══════════════════════════════════════════════════════ function test_relay_revert_chainIncorrect() public { @@ -150,4 +167,9 @@ contract FastBridgeV2DstTest is FastBridgeV2Test { vm.expectRevert(DeadlineExceeded.selector); relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: 0, bridgeTx: tokenTx}); } + + function test_relay_withRelayerAddress_revert_zeroAddr() public { + vm.expectRevert(ZeroAddress.selector); + relayWithAddress({caller: relayerA, relayer: address(0), msgValue: 0, bridgeTx: tokenTx}); + } } From 1c5e21d7fdffbeee54e4f425cc494a99d54aaf0e Mon Sep 17 00:00:00 2001 From: parodime Date: Thu, 26 Sep 2024 16:06:50 +0000 Subject: [PATCH 43/90] Publish - @synapsecns/contracts-rfq@0.5.5 --- packages/contracts-rfq/CHANGELOG.md | 8 ++++++++ packages/contracts-rfq/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/contracts-rfq/CHANGELOG.md b/packages/contracts-rfq/CHANGELOG.md index 3743ed0540..b6513a6e3a 100644 --- a/packages/contracts-rfq/CHANGELOG.md +++ b/packages/contracts-rfq/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.5](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.4...@synapsecns/contracts-rfq@0.5.5) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/contracts-rfq + + + + + ## [0.5.4](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.3...@synapsecns/contracts-rfq@0.5.4) (2024-09-26) **Note:** Version bump only for package @synapsecns/contracts-rfq diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index 76f4073981..a0086dc914 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/contracts-rfq", "license": "MIT", - "version": "0.5.4", + "version": "0.5.5", "description": "FastBridge contracts.", "private": true, "files": [ From 4a45bcf3e73e39dbf81bdea877029bab7fe2ca86 Mon Sep 17 00:00:00 2001 From: abtestingalpha <104046418+abtestingalpha@users.noreply.github.com> Date: Thu, 26 Sep 2024 12:39:55 -0400 Subject: [PATCH 44/90] Adjust text to trigger build (#3199) --- packages/synapse-interface/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/synapse-interface/README.md b/packages/synapse-interface/README.md index 4023fc2c54..ca91c23a00 100644 --- a/packages/synapse-interface/README.md +++ b/packages/synapse-interface/README.md @@ -2,7 +2,7 @@ This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next ## Getting Started -First, run the development server: +First, run development server: ```bash yarn dev From d8338fec157da786f663b1970076f3621db1ea3d Mon Sep 17 00:00:00 2001 From: abtestingalpha Date: Thu, 26 Sep 2024 16:44:17 +0000 Subject: [PATCH 45/90] Publish - @synapsecns/synapse-interface@0.39.2 --- packages/synapse-interface/CHANGELOG.md | 8 ++++++++ packages/synapse-interface/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/synapse-interface/CHANGELOG.md b/packages/synapse-interface/CHANGELOG.md index 516afa1d79..77af15ca8a 100644 --- a/packages/synapse-interface/CHANGELOG.md +++ b/packages/synapse-interface/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.39.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.39.1...@synapsecns/synapse-interface@0.39.2) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/synapse-interface + + + + + ## [0.39.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.39.0...@synapsecns/synapse-interface@0.39.1) (2024-09-26) **Note:** Version bump only for package @synapsecns/synapse-interface diff --git a/packages/synapse-interface/package.json b/packages/synapse-interface/package.json index 1424a86ea4..837ced3014 100644 --- a/packages/synapse-interface/package.json +++ b/packages/synapse-interface/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/synapse-interface", - "version": "0.39.1", + "version": "0.39.2", "private": true, "engines": { "node": ">=18.18.0" From f0b13bc456620004a1787f62e87f404d95272356 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Thu, 26 Sep 2024 10:37:34 -0700 Subject: [PATCH 46/90] feat(synapse-interface): refund RFQ transaction [SLT-272] (#3197) * Txn transaction refund tracking * Update store to support tracking * Query FastBridge contract for `bridgeStatuses` to find refund status * Track bridge transaction `bridgeQuote.routerAddress` in store * Fetch FastBridge contract address when only provided router address * add translations --- .../components/_Transaction/_Transaction.tsx | 21 +- .../components/_Transaction/_Transactions.tsx | 1 + .../components/AnimatedProgressBar.tsx | 6 +- .../_Transaction/components/TimeRemaining.tsx | 10 +- .../components/TransactionSupport.tsx | 17 +- .../helpers/calculateEstimatedTimeStatus.ts | 3 + .../helpers/useBridgeTxUpdater.ts | 11 +- .../_Transaction/helpers/useTxRefundStatus.ts | 103 +++ .../constants/abis/fastBridge.json | 851 ++++++++++++++++++ .../constants/abis/fastBridgeRouter.json | 387 ++++++++ packages/synapse-interface/messages/ar.json | 2 + .../synapse-interface/messages/en-US.json | 2 + packages/synapse-interface/messages/es.json | 2 + packages/synapse-interface/messages/fr.json | 2 + packages/synapse-interface/messages/jp.json | 2 + packages/synapse-interface/messages/tr.json | 2 + .../synapse-interface/messages/zh-CN.json | 2 + .../pages/state-managed-bridge/index.tsx | 1 + .../slices/_transactions/reducer.ts | 17 +- .../slices/transactions/actions.ts | 1 + .../utils/hooks/use_TransactionsListener.ts | 1 + 21 files changed, 1430 insertions(+), 14 deletions(-) create mode 100644 packages/synapse-interface/components/_Transaction/helpers/useTxRefundStatus.ts create mode 100644 packages/synapse-interface/constants/abis/fastBridge.json create mode 100644 packages/synapse-interface/constants/abis/fastBridgeRouter.json diff --git a/packages/synapse-interface/components/_Transaction/_Transaction.tsx b/packages/synapse-interface/components/_Transaction/_Transaction.tsx index 1a1158825d..1aea90352b 100644 --- a/packages/synapse-interface/components/_Transaction/_Transaction.tsx +++ b/packages/synapse-interface/components/_Transaction/_Transaction.tsx @@ -19,6 +19,7 @@ import { TransactionSupport } from './components/TransactionSupport' import { RightArrow } from '@/components/icons/RightArrow' import { Address } from 'viem' import { useIsTxReverted } from './helpers/useIsTxReverted' +import { useTxRefundStatus } from './helpers/useTxRefundStatus' interface _TransactionProps { connectedAddress: string @@ -30,11 +31,12 @@ interface _TransactionProps { destinationToken: Token originTxHash: string bridgeModuleName: string + routerAddress: string estimatedTime: number // in seconds timestamp: number currentTime: number kappa?: string - status: 'pending' | 'completed' | 'reverted' + status: 'pending' | 'completed' | 'reverted' | 'refunded' disabled: boolean } @@ -49,6 +51,7 @@ export const _Transaction = ({ destinationToken, originTxHash, bridgeModuleName, + routerAddress, estimatedTime, timestamp, currentTime, @@ -80,6 +83,7 @@ export const _Transaction = ({ isEstimatedTimeReached, isCheckTxStatus, isCheckTxForRevert, + isCheckTxForRefund, } = calculateEstimatedTimeStatus(currentTime, timestamp, estimatedTime) const [isTxCompleted, _kappa] = useBridgeTxStatus({ @@ -98,18 +102,29 @@ export const _Transaction = ({ isCheckTxForRevert && status === 'pending' ) + const isTxRefunded = useTxRefundStatus( + kappa, + routerAddress as Address, + originChain, + isCheckTxForRefund && + status === 'pending' && + bridgeModuleName === 'SynapseRFQ' + ) + useBridgeTxUpdater( connectedAddress, destinationChain, _kappa, originTxHash, isTxCompleted, - isTxReverted + isTxReverted, + isTxRefunded ) // Show transaction support if the transaction is delayed by more than 5 minutes and not finalized or reverted const showTransactionSupport = status === 'reverted' || + status === 'refunded' || (status === 'pending' && delayedTimeInMin && delayedTimeInMin <= -5) return ( @@ -184,7 +199,7 @@ export const _Transaction = ({ {status !== 'pending' && ( { const currentTime = getUnixTimeMinutesBeforeNow(0) const elapsedTime = currentTime - startTime @@ -25,7 +25,7 @@ export const AnimatedProgressBar = memo( const percentElapsed = (elapsedTime / estDuration) * 100 const isComplete = status === 'completed' - const isError = status === 'reverted' + const isError = status === 'reverted' || status === 'refunded' let duration = isComplete ? 0.5 : remainingTime diff --git a/packages/synapse-interface/components/_Transaction/components/TimeRemaining.tsx b/packages/synapse-interface/components/_Transaction/components/TimeRemaining.tsx index fda528f15c..f7920c9473 100644 --- a/packages/synapse-interface/components/_Transaction/components/TimeRemaining.tsx +++ b/packages/synapse-interface/components/_Transaction/components/TimeRemaining.tsx @@ -12,7 +12,7 @@ export const TimeRemaining = ({ isDelayed: boolean remainingTime: number delayedTime: number | null - status: 'pending' | 'completed' | 'reverted' + status: 'pending' | 'completed' | 'reverted' | 'refunded' }) => { const t = useTranslations('Time') @@ -36,6 +36,14 @@ export const TimeRemaining = ({ ) } + if (status === 'refunded') { + return ( + + {t('Refunded')} + + ) + } + if (isDelayed) { const delayedTimeInMin = Math.floor(delayedTime / 60) const absoluteDelayedTime = Math.abs(delayedTimeInMin) diff --git a/packages/synapse-interface/components/_Transaction/components/TransactionSupport.tsx b/packages/synapse-interface/components/_Transaction/components/TransactionSupport.tsx index 2803d73911..02b09ab5fa 100644 --- a/packages/synapse-interface/components/_Transaction/components/TransactionSupport.tsx +++ b/packages/synapse-interface/components/_Transaction/components/TransactionSupport.tsx @@ -1,8 +1,11 @@ import { useTranslations } from 'next-intl' import { TRANSACTION_SUPPORT_URL, DISCORD_URL } from '@/constants/urls' -export const TransactionSupport = ({ status }: { status: string }) => { - const isReverted = status === 'reverted' +export const TransactionSupport = ({ + status, +}: { + status: 'pending' | 'completed' | 'reverted' | 'refunded' +}) => { const t = useTranslations('Time') return ( @@ -10,12 +13,16 @@ export const TransactionSupport = ({ status }: { status: string }) => { id="transaction-support" className="flex items-center justify-between w-full py-1 pl-3 pr-1 text-sm" > - {isReverted ? ( + {status === 'reverted' && (
{t('Transaction reverted, funds returned')}
- ) : ( -
{t("What's taking so long?")}
)} + {status === 'refunded' && ( +
{t('Transaction refunded, funds returned')}
+ )} + + {status === 'pending' &&
{t("What's taking so long?")}
} +
30 + const isCheckTxForRefund = elapsedTime > fourHoursInSeconds const delayedTime = isEstimatedTimeReached ? remainingTime : null const delayedTimeInMin = remainingTime ? Math.floor(remainingTime / 60) : null @@ -33,5 +35,6 @@ export const calculateEstimatedTimeStatus = ( isEstimatedTimeReached, isCheckTxStatus, isCheckTxForRevert, + isCheckTxForRefund, } } diff --git a/packages/synapse-interface/components/_Transaction/helpers/useBridgeTxUpdater.ts b/packages/synapse-interface/components/_Transaction/helpers/useBridgeTxUpdater.ts index f493893111..9466b9f670 100644 --- a/packages/synapse-interface/components/_Transaction/helpers/useBridgeTxUpdater.ts +++ b/packages/synapse-interface/components/_Transaction/helpers/useBridgeTxUpdater.ts @@ -5,6 +5,7 @@ import { updateTransactionKappa, completeTransaction, revertTransaction, + refundTransaction, _TransactionDetails, } from '@/slices/_transactions/reducer' import { fetchAndStoreSingleNetworkPortfolioBalances } from '@/slices/portfolio/hooks' @@ -27,7 +28,8 @@ export const useBridgeTxUpdater = ( kappa: string, originTxHash: string, isTxComplete: boolean, - isTxReverted: boolean + isTxReverted: boolean, + isTxRefunded: boolean ) => { const dispatch = useAppDispatch() const { transactions } = use_TransactionsState() @@ -49,6 +51,13 @@ export const useBridgeTxUpdater = ( } }, [isTxReverted]) + /** Update tx for refunds in store */ + useEffect(() => { + if (isTxRefunded && storedTx.status !== 'refunded') { + dispatch(refundTransaction({ originTxHash })) + } + }, [isTxRefunded]) + /** Update tx for completion in store */ useEffect(() => { if (isTxComplete && originTxHash && kappa) { diff --git a/packages/synapse-interface/components/_Transaction/helpers/useTxRefundStatus.ts b/packages/synapse-interface/components/_Transaction/helpers/useTxRefundStatus.ts new file mode 100644 index 0000000000..351f97f04e --- /dev/null +++ b/packages/synapse-interface/components/_Transaction/helpers/useTxRefundStatus.ts @@ -0,0 +1,103 @@ +import { type Address } from 'viem' +import { isNumber, isString } from 'lodash' +import { useEffect, useState } from 'react' +import { readContract } from '@wagmi/core' + +import { type Chain } from '@/utils/types' +import { useIntervalTimer } from '@/utils/hooks/useIntervalTimer' +import { wagmiConfig } from '@/wagmiConfig' +import fastBridgeAbi from '@/constants/abis/fastBridge.json' +import fastBridgeRouterAbi from '@/constants/abis/fastBridgeRouter.json' + +enum BridgeStatus { + NULL, + REQUESTED, + RELAYER_PROVED, + RELAYER_CLAIMED, + REFUNDED, +} + +export const useTxRefundStatus = ( + txId: string | undefined, + routerAddress: Address, + chain: Chain, + checkForRefund: boolean +) => { + const [isRefunded, setIsRefunded] = useState(false) + const currentTime = useIntervalTimer(600000) + + const getTxRefundStatus = async () => { + try { + const bridgeContract = await getRFQBridgeContract( + routerAddress, + chain?.id + ) + + const status = await checkRFQTxBridgeStatus( + txId, + bridgeContract as Address, + chain?.id + ) + + if (status === BridgeStatus.REFUNDED) { + setIsRefunded(true) + } + } catch (error) { + console.error('Failed to get transaction refund status:', error) + } + } + + useEffect(() => { + if (checkForRefund) { + getTxRefundStatus() + } + }, [checkForRefund, txId, chain, currentTime]) + + return isRefunded +} + +const getRFQBridgeContract = async ( + routerAddress: Address, + chainId: number +): Promise => { + try { + const fastBridgeAddress = await readContract(wagmiConfig, { + abi: fastBridgeRouterAbi, + address: routerAddress, + functionName: 'fastBridge', + chainId, + }) + + if (!isString(fastBridgeAddress)) { + throw new Error('Invalid address') + } + + return fastBridgeAddress + } catch (error) { + throw new Error(error) + } +} + +const checkRFQTxBridgeStatus = async ( + txId: string, + bridgeContract: Address, + chainId: number +): Promise => { + try { + const status = await readContract(wagmiConfig, { + abi: fastBridgeAbi, + address: bridgeContract, + functionName: 'bridgeStatuses', + args: [txId], + chainId, + }) + + if (!isNumber(status)) { + throw new Error('Invalid status code') + } + + return status + } catch (error) { + throw new Error(error) + } +} diff --git a/packages/synapse-interface/constants/abis/fastBridge.json b/packages/synapse-interface/constants/abis/fastBridge.json new file mode 100644 index 0000000000..2efeb97a4b --- /dev/null +++ b/packages/synapse-interface/constants/abis/fastBridge.json @@ -0,0 +1,851 @@ +[ + { + "type": "constructor", + "inputs": [ + { "name": "_owner", "type": "address", "internalType": "address" } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "DEFAULT_ADMIN_ROLE", + "inputs": [], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "DISPUTE_PERIOD", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "FEE_BPS", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "FEE_RATE_MAX", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "GOVERNOR_ROLE", + "inputs": [], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "GUARD_ROLE", + "inputs": [], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "MIN_DEADLINE_PERIOD", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "REFUNDER_ROLE", + "inputs": [], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "REFUND_DELAY", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "RELAYER_ROLE", + "inputs": [], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "bridge", + "inputs": [ + { + "name": "params", + "type": "tuple", + "internalType": "struct IFastBridge.BridgeParams", + "components": [ + { + "name": "dstChainId", + "type": "uint32", + "internalType": "uint32" + }, + { "name": "sender", "type": "address", "internalType": "address" }, + { "name": "to", "type": "address", "internalType": "address" }, + { + "name": "originToken", + "type": "address", + "internalType": "address" + }, + { + "name": "destToken", + "type": "address", + "internalType": "address" + }, + { + "name": "originAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "destAmount", + "type": "uint256", + "internalType": "uint256" + }, + { "name": "sendChainGas", "type": "bool", "internalType": "bool" }, + { "name": "deadline", "type": "uint256", "internalType": "uint256" } + ] + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "bridgeProofs", + "inputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "outputs": [ + { "name": "timestamp", "type": "uint96", "internalType": "uint96" }, + { "name": "relayer", "type": "address", "internalType": "address" } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "bridgeRelays", + "inputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "bridgeStatuses", + "inputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "outputs": [ + { + "name": "", + "type": "uint8", + "internalType": "enum FastBridge.BridgeStatus" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "canClaim", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "internalType": "bytes32" + }, + { "name": "relayer", "type": "address", "internalType": "address" } + ], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "chainGasAmount", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "claim", + "inputs": [ + { "name": "request", "type": "bytes", "internalType": "bytes" }, + { "name": "to", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "deployBlock", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "dispute", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "getBridgeTransaction", + "inputs": [ + { "name": "request", "type": "bytes", "internalType": "bytes" } + ], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct IFastBridge.BridgeTransaction", + "components": [ + { + "name": "originChainId", + "type": "uint32", + "internalType": "uint32" + }, + { + "name": "destChainId", + "type": "uint32", + "internalType": "uint32" + }, + { + "name": "originSender", + "type": "address", + "internalType": "address" + }, + { + "name": "destRecipient", + "type": "address", + "internalType": "address" + }, + { + "name": "originToken", + "type": "address", + "internalType": "address" + }, + { + "name": "destToken", + "type": "address", + "internalType": "address" + }, + { + "name": "originAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "destAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "originFeeAmount", + "type": "uint256", + "internalType": "uint256" + }, + { "name": "sendChainGas", "type": "bool", "internalType": "bool" }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { "name": "nonce", "type": "uint256", "internalType": "uint256" } + ] + } + ], + "stateMutability": "pure" + }, + { + "type": "function", + "name": "getRoleAdmin", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" } + ], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getRoleMember", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" }, + { "name": "index", "type": "uint256", "internalType": "uint256" } + ], + "outputs": [{ "name": "", "type": "address", "internalType": "address" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getRoleMemberCount", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" } + ], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "grantRole", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" }, + { "name": "account", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "hasRole", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" }, + { "name": "account", "type": "address", "internalType": "address" } + ], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "nonce", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "protocolFeeRate", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "protocolFees", + "inputs": [{ "name": "", "type": "address", "internalType": "address" }], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "prove", + "inputs": [ + { "name": "request", "type": "bytes", "internalType": "bytes" }, + { "name": "destTxHash", "type": "bytes32", "internalType": "bytes32" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "refund", + "inputs": [ + { "name": "request", "type": "bytes", "internalType": "bytes" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "relay", + "inputs": [ + { "name": "request", "type": "bytes", "internalType": "bytes" } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "renounceRole", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" }, + { + "name": "callerConfirmation", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "revokeRole", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" }, + { "name": "account", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setChainGasAmount", + "inputs": [ + { + "name": "newChainGasAmount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setProtocolFeeRate", + "inputs": [ + { "name": "newFeeRate", "type": "uint256", "internalType": "uint256" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "supportsInterface", + "inputs": [ + { "name": "interfaceId", "type": "bytes4", "internalType": "bytes4" } + ], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "sweepProtocolFees", + "inputs": [ + { "name": "token", "type": "address", "internalType": "address" }, + { "name": "recipient", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "BridgeDepositClaimed", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "relayer", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "token", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BridgeDepositRefunded", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "token", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BridgeProofDisputed", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "relayer", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BridgeProofProvided", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "relayer", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "transactionHash", + "type": "bytes32", + "indexed": false, + "internalType": "bytes32" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BridgeRelayed", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "relayer", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "originChainId", + "type": "uint32", + "indexed": false, + "internalType": "uint32" + }, + { + "name": "originToken", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "destToken", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "originAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "destAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "chainGasAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BridgeRequested", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "request", + "type": "bytes", + "indexed": false, + "internalType": "bytes" + }, + { + "name": "destChainId", + "type": "uint32", + "indexed": false, + "internalType": "uint32" + }, + { + "name": "originToken", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "destToken", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "originAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "destAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "sendChainGas", + "type": "bool", + "indexed": false, + "internalType": "bool" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ChainGasAmountUpdated", + "inputs": [ + { + "name": "oldChainGasAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "newChainGasAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "FeeRateUpdated", + "inputs": [ + { + "name": "oldFeeRate", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "newFeeRate", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "FeesSwept", + "inputs": [ + { + "name": "token", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RoleAdminChanged", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "previousAdminRole", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "newAdminRole", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RoleGranted", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "account", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RoleRevoked", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "account", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { "type": "error", "name": "AccessControlBadConfirmation", "inputs": [] }, + { + "type": "error", + "name": "AccessControlUnauthorizedAccount", + "inputs": [ + { "name": "account", "type": "address", "internalType": "address" }, + { "name": "neededRole", "type": "bytes32", "internalType": "bytes32" } + ] + }, + { + "type": "error", + "name": "AddressEmptyCode", + "inputs": [ + { "name": "target", "type": "address", "internalType": "address" } + ] + }, + { + "type": "error", + "name": "AddressInsufficientBalance", + "inputs": [ + { "name": "account", "type": "address", "internalType": "address" } + ] + }, + { "type": "error", "name": "AmountIncorrect", "inputs": [] }, + { "type": "error", "name": "ChainIncorrect", "inputs": [] }, + { "type": "error", "name": "DeadlineExceeded", "inputs": [] }, + { "type": "error", "name": "DeadlineNotExceeded", "inputs": [] }, + { "type": "error", "name": "DeadlineTooShort", "inputs": [] }, + { "type": "error", "name": "DisputePeriodNotPassed", "inputs": [] }, + { "type": "error", "name": "DisputePeriodPassed", "inputs": [] }, + { "type": "error", "name": "FailedInnerCall", "inputs": [] }, + { "type": "error", "name": "MsgValueIncorrect", "inputs": [] }, + { + "type": "error", + "name": "SafeERC20FailedOperation", + "inputs": [ + { "name": "token", "type": "address", "internalType": "address" } + ] + }, + { "type": "error", "name": "SenderIncorrect", "inputs": [] }, + { "type": "error", "name": "StatusIncorrect", "inputs": [] }, + { "type": "error", "name": "TokenNotContract", "inputs": [] }, + { "type": "error", "name": "TransactionRelayed", "inputs": [] }, + { "type": "error", "name": "ZeroAddress", "inputs": [] } +] diff --git a/packages/synapse-interface/constants/abis/fastBridgeRouter.json b/packages/synapse-interface/constants/abis/fastBridgeRouter.json new file mode 100644 index 0000000000..c5617fe1cf --- /dev/null +++ b/packages/synapse-interface/constants/abis/fastBridgeRouter.json @@ -0,0 +1,387 @@ +[ + { + "type": "constructor", + "inputs": [ + { + "name": "owner_", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "receive", + "stateMutability": "payable" + }, + { + "type": "function", + "name": "GAS_REBATE_FLAG", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes1", + "internalType": "bytes1" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "adapterSwap", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "amountOut", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "bridge", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "chainId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "originQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + }, + { + "name": "destQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "fastBridge", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getOriginAmountOut", + "inputs": [ + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "rfqTokens", + "type": "address[]", + "internalType": "address[]" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "originQueries", + "type": "tuple[]", + "internalType": "struct SwapQuery[]", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setFastBridge", + "inputs": [ + { + "name": "fastBridge_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setSwapQuoter", + "inputs": [ + { + "name": "swapQuoter_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "swapQuoter", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "FastBridgeSet", + "inputs": [ + { + "name": "newFastBridge", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SwapQuoterSet", + "inputs": [ + { + "name": "newSwapQuoter", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "DeadlineExceeded", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientOutputAmount", + "inputs": [] + }, + { + "type": "error", + "name": "MsgValueIncorrect", + "inputs": [] + }, + { + "type": "error", + "name": "PoolNotFound", + "inputs": [] + }, + { + "type": "error", + "name": "TokenAddressMismatch", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotContract", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotETH", + "inputs": [] + }, + { + "type": "error", + "name": "TokensIdentical", + "inputs": [] + } +] diff --git a/packages/synapse-interface/messages/ar.json b/packages/synapse-interface/messages/ar.json index fc6571c49e..a98de789cf 100644 --- a/packages/synapse-interface/messages/ar.json +++ b/packages/synapse-interface/messages/ar.json @@ -306,7 +306,9 @@ "Began": "بدأ", "Complete": "مكتمل", "Reverted": "تم الرجوع", + "Refunded": "تم استرداده", "Transaction reverted, funds returned": "تم الرجوع عن المعاملة، تم إرجاع الأموال", + "Transaction refunded, funds returned": "تم استرداد المعاملة، وتمت إعادة الأموال", "What's taking so long?": "لماذا يستغرق الأمر وقتًا طويلًا؟", "FAQ": "الأسئلة الشائعة", "Support": "الدعم", diff --git a/packages/synapse-interface/messages/en-US.json b/packages/synapse-interface/messages/en-US.json index 7c5a99c5d0..8ffc76e4a1 100644 --- a/packages/synapse-interface/messages/en-US.json +++ b/packages/synapse-interface/messages/en-US.json @@ -306,7 +306,9 @@ "Began": "Began", "Complete": "Complete", "Reverted": "Reverted", + "Refunded": "Refunded", "Transaction reverted, funds returned": "Transaction reverted, funds returned", + "Transaction refunded, funds returned": "Transaction refunded, funds returned", "What's taking so long?": "What's taking so long?", "FAQ": "F.A.Q", "Support": "Support", diff --git a/packages/synapse-interface/messages/es.json b/packages/synapse-interface/messages/es.json index c6ac15e7d8..d19e0158c7 100644 --- a/packages/synapse-interface/messages/es.json +++ b/packages/synapse-interface/messages/es.json @@ -306,7 +306,9 @@ "Began": "Comenzó", "Complete": "Completo", "Reverted": "Revertido", + "Refunded": "Reembolsado", "Transaction reverted, funds returned": "Transacción revertida, fondos devueltos", + "Transaction refunded, funds returned": "Transacción reembolsada, fondos devueltos", "What's taking so long?": "¿Por qué está tardando tanto?", "FAQ": "Preguntas frecuentes", "Support": "Soporte", diff --git a/packages/synapse-interface/messages/fr.json b/packages/synapse-interface/messages/fr.json index b5f249aaf1..8164e65434 100644 --- a/packages/synapse-interface/messages/fr.json +++ b/packages/synapse-interface/messages/fr.json @@ -306,7 +306,9 @@ "Began": "Commencé", "Complete": "Terminé", "Reverted": "Annulé", + "Refunded": "Remboursé", "Transaction reverted, funds returned": "Transaction annulée, fonds retournés", + "Transaction refunded, funds returned": "Transaction remboursée, fonds retournés", "What's taking so long?": "Qu'est-ce qui prend tant de temps ?", "FAQ": "FAQ", "Support": "Support", diff --git a/packages/synapse-interface/messages/jp.json b/packages/synapse-interface/messages/jp.json index c187de083b..b5b97ef750 100644 --- a/packages/synapse-interface/messages/jp.json +++ b/packages/synapse-interface/messages/jp.json @@ -306,7 +306,9 @@ "Began": "開始", "Complete": "完了", "Reverted": "取り消し", + "Refunded": "返金済み", "Transaction reverted, funds returned": "取引が取り消されました。資金が返還されました", + "Transaction refunded, funds returned": "取引が返金され、資金が戻されました", "What's taking so long?": "なぜ時間がかかっているのですか?", "FAQ": "よくある質問", "Support": "サポート", diff --git a/packages/synapse-interface/messages/tr.json b/packages/synapse-interface/messages/tr.json index dbe44d71b2..2afcf1ec6b 100644 --- a/packages/synapse-interface/messages/tr.json +++ b/packages/synapse-interface/messages/tr.json @@ -306,7 +306,9 @@ "Began": "Başladı", "Complete": "Tamamlandı", "Reverted": "Geri Alındı", + "Refunded": "İade edildi", "Transaction reverted, funds returned": "İşlem geri alındı, fonlar iade edildi", + "Transaction refunded, funds returned": "İşlem iade edildi, fonlar geri gönderildi", "What's taking so long?": "Neden bu kadar uzun sürüyor?", "FAQ": "SSS", "Support": "Destek", diff --git a/packages/synapse-interface/messages/zh-CN.json b/packages/synapse-interface/messages/zh-CN.json index 0d5cb9e02d..639c863131 100644 --- a/packages/synapse-interface/messages/zh-CN.json +++ b/packages/synapse-interface/messages/zh-CN.json @@ -306,7 +306,9 @@ "Began": "已开始", "Complete": "已完成", "Reverted": "已回退", + "Refunded": "已退款", "Transaction reverted, funds returned": "交易已回退,资金已退还", + "Transaction refunded, funds returned": "交易已退款,资金已退还", "What's taking so long?": "为什么这么久?", "FAQ": "常见问题", "Support": "支持", diff --git a/packages/synapse-interface/pages/state-managed-bridge/index.tsx b/packages/synapse-interface/pages/state-managed-bridge/index.tsx index e3128e0d6d..2a63c4ba56 100644 --- a/packages/synapse-interface/pages/state-managed-bridge/index.tsx +++ b/packages/synapse-interface/pages/state-managed-bridge/index.tsx @@ -302,6 +302,7 @@ const StateManagedBridge = () => { estimatedTime: bridgeQuote.estimatedTime, bridgeModuleName: bridgeQuote.bridgeModuleName, destinationAddress: destinationAddress, + routerAddress: bridgeQuote.routerAddress, }) ) try { diff --git a/packages/synapse-interface/slices/_transactions/reducer.ts b/packages/synapse-interface/slices/_transactions/reducer.ts index 2225c8ead0..cac413c616 100644 --- a/packages/synapse-interface/slices/_transactions/reducer.ts +++ b/packages/synapse-interface/slices/_transactions/reducer.ts @@ -14,10 +14,11 @@ export interface _TransactionDetails { originValue: string originTxHash: string bridgeModuleName: string + routerAddress: string estimatedTime: number timestamp: number kappa?: string - status: 'pending' | 'completed' | 'reverted' + status: 'pending' | 'completed' | 'reverted' | 'refunded' } export interface _TransactionsState { @@ -87,6 +88,19 @@ export const transactionsSlice = createSlice({ state.transactions[txIndex].status = 'reverted' } }, + refundTransaction: ( + state, + action: PayloadAction<{ originTxHash: string }> + ) => { + const { originTxHash } = action.payload + + const txIndex = state.transactions.findIndex( + (tx) => tx.originTxHash === originTxHash + ) + if (txIndex !== -1) { + state.transactions[txIndex].status = 'refunded' + } + }, clearTransactions: (state) => { state.transactions = [] }, @@ -99,6 +113,7 @@ export const { updateTransactionKappa, completeTransaction, revertTransaction, + refundTransaction, clearTransactions, } = transactionsSlice.actions diff --git a/packages/synapse-interface/slices/transactions/actions.ts b/packages/synapse-interface/slices/transactions/actions.ts index cb4ef22cc7..68400faaa9 100644 --- a/packages/synapse-interface/slices/transactions/actions.ts +++ b/packages/synapse-interface/slices/transactions/actions.ts @@ -16,6 +16,7 @@ export interface PendingBridgeTransaction { isSubmitted: boolean estimatedTime: number bridgeModuleName: string + routerAddress: string destinationAddress: Address | null } diff --git a/packages/synapse-interface/utils/hooks/use_TransactionsListener.ts b/packages/synapse-interface/utils/hooks/use_TransactionsListener.ts index b82d6ca8fe..e2f7b85697 100644 --- a/packages/synapse-interface/utils/hooks/use_TransactionsListener.ts +++ b/packages/synapse-interface/utils/hooks/use_TransactionsListener.ts @@ -50,6 +50,7 @@ export const use_TransactionsListener = () => { destinationChain: tx.destinationChain, destinationToken: tx.destinationToken, bridgeModuleName: tx.bridgeModuleName, + routerAddress: tx.routerAddress, estimatedTime: tx.estimatedTime, timestamp: tx.id, status: 'pending', From 96bf50f6c8cfeacf002052eb7e8cac986e6a16ab Mon Sep 17 00:00:00 2001 From: abtestingalpha Date: Thu, 26 Sep 2024 17:42:03 +0000 Subject: [PATCH 47/90] Publish - @synapsecns/synapse-interface@0.40.0 --- packages/synapse-interface/CHANGELOG.md | 11 +++++++++++ packages/synapse-interface/package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/synapse-interface/CHANGELOG.md b/packages/synapse-interface/CHANGELOG.md index 77af15ca8a..8af7d86ed2 100644 --- a/packages/synapse-interface/CHANGELOG.md +++ b/packages/synapse-interface/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.40.0](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.39.2...@synapsecns/synapse-interface@0.40.0) (2024-09-26) + + +### Features + +* **synapse-interface:** refund RFQ transaction [SLT-272] ([#3197](https://github.com/synapsecns/sanguine/issues/3197)) ([f0b13bc](https://github.com/synapsecns/sanguine/commit/f0b13bc456620004a1787f62e87f404d95272356)) + + + + + ## [0.39.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.39.1...@synapsecns/synapse-interface@0.39.2) (2024-09-26) **Note:** Version bump only for package @synapsecns/synapse-interface diff --git a/packages/synapse-interface/package.json b/packages/synapse-interface/package.json index 837ced3014..586401902a 100644 --- a/packages/synapse-interface/package.json +++ b/packages/synapse-interface/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/synapse-interface", - "version": "0.39.2", + "version": "0.40.0", "private": true, "engines": { "node": ">=18.18.0" From e897b188097756676a3ab988ac2913e64f238e45 Mon Sep 17 00:00:00 2001 From: parodime Date: Thu, 26 Sep 2024 14:02:56 -0400 Subject: [PATCH 48/90] no empty sender recip [SLT-184] (#3171) * no empty sender/to addrs [SLT-184] * enable tests * code reformat --- packages/contracts-rfq/contracts/FastBridgeV2.sol | 1 + packages/contracts-rfq/test/FastBridgeV2.Src.t.sol | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/contracts-rfq/contracts/FastBridgeV2.sol b/packages/contracts-rfq/contracts/FastBridgeV2.sol index adf48dd1f3..a8a6f819e7 100644 --- a/packages/contracts-rfq/contracts/FastBridgeV2.sol +++ b/packages/contracts-rfq/contracts/FastBridgeV2.sol @@ -80,6 +80,7 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { // check bridge params if (params.dstChainId == block.chainid) revert ChainIncorrect(); if (params.originAmount == 0 || params.destAmount == 0) revert AmountIncorrect(); + if (params.sender == address(0) || params.to == address(0)) revert ZeroAddress(); if (params.originToken == address(0) || params.destToken == address(0)) revert ZeroAddress(); if (params.deadline < block.timestamp + MIN_DEADLINE_PERIOD) revert DeadlineTooShort(); diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol index 6e433a1274..05844821e7 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol @@ -275,14 +275,12 @@ contract FastBridgeV2SrcTest is FastBridgeV2Test { } function test_bridge_revert_zeroSender() public { - vm.skip(true); // TODO: unskip when fixed tokenParams.sender = address(0); vm.expectRevert(ZeroAddress.selector); bridge({caller: userA, msgValue: 0, params: tokenParams}); } function test_bridge_revert_zeroRecipient() public { - vm.skip(true); // TODO: unskip when fixed tokenParams.to = address(0); vm.expectRevert(ZeroAddress.selector); bridge({caller: userA, msgValue: 0, params: tokenParams}); From ebf102ce9208effca37ee181713f7cd436711877 Mon Sep 17 00:00:00 2001 From: parodime Date: Thu, 26 Sep 2024 18:06:59 +0000 Subject: [PATCH 49/90] Publish - @synapsecns/contracts-rfq@0.5.6 --- packages/contracts-rfq/CHANGELOG.md | 8 ++++++++ packages/contracts-rfq/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/contracts-rfq/CHANGELOG.md b/packages/contracts-rfq/CHANGELOG.md index b6513a6e3a..feec5d7356 100644 --- a/packages/contracts-rfq/CHANGELOG.md +++ b/packages/contracts-rfq/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.6](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.5...@synapsecns/contracts-rfq@0.5.6) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/contracts-rfq + + + + + ## [0.5.5](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.4...@synapsecns/contracts-rfq@0.5.5) (2024-09-26) **Note:** Version bump only for package @synapsecns/contracts-rfq diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index a0086dc914..b0c33eafd4 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/contracts-rfq", "license": "MIT", - "version": "0.5.5", + "version": "0.5.6", "description": "FastBridge contracts.", "private": true, "files": [ From 7bcb5d9c9256ae099fa1d93bd0955eae500a55d1 Mon Sep 17 00:00:00 2001 From: parodime Date: Thu, 26 Sep 2024 14:29:20 -0400 Subject: [PATCH 50/90] rearrange refund validation checks [SLT-185] (#3172) * rearrange refund validation checks [SLT-185] * test: fix own incorrectly defined tests --------- Co-authored-by: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> --- packages/contracts-rfq/contracts/FastBridgeV2.sol | 6 ++++-- packages/contracts-rfq/test/FastBridgeV2.Src.t.sol | 9 +++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/contracts-rfq/contracts/FastBridgeV2.sol b/packages/contracts-rfq/contracts/FastBridgeV2.sol index a8a6f819e7..a92c0746f0 100644 --- a/packages/contracts-rfq/contracts/FastBridgeV2.sol +++ b/packages/contracts-rfq/contracts/FastBridgeV2.sol @@ -274,6 +274,9 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { /// @inheritdoc IFastBridge function refund(bytes memory request) external { bytes32 transactionId = keccak256(request); + + if (bridgeStatuses[transactionId] != BridgeStatus.REQUESTED) revert StatusIncorrect(); + BridgeTransaction memory transaction = getBridgeTransaction(request); if (hasRole(REFUNDER_ROLE, msg.sender)) { @@ -284,8 +287,7 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { if (block.timestamp <= transaction.deadline + REFUND_DELAY) revert DeadlineNotExceeded(); } - // set status to refunded if still in requested state - if (bridgeStatuses[transactionId] != BridgeStatus.REQUESTED) revert StatusIncorrect(); + // if all checks passed, set to REFUNDED status bridgeStatuses[transactionId] = BridgeStatus.REFUNDED; // transfer origin collateral back to original sender diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol index 05844821e7..d1336885e3 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol @@ -896,14 +896,14 @@ contract FastBridgeV2SrcTest is FastBridgeV2Test { function test_refund_revert_zeroDelay() public { bridge({caller: userA, msgValue: 0, params: tokenParams}); vm.expectRevert(DeadlineNotExceeded.selector); - refund({caller: refunder, bridgeTx: ethTx}); + refund({caller: refunder, bridgeTx: tokenTx}); } function test_refund_revert_justBeforeDeadline() public { bridge({caller: userA, msgValue: 0, params: tokenParams}); skip(DEADLINE); vm.expectRevert(DeadlineNotExceeded.selector); - refund({caller: refunder, bridgeTx: ethTx}); + refund({caller: refunder, bridgeTx: tokenTx}); } function test_refund_revert_justBeforeDeadline_permisionless(address caller) public { @@ -911,17 +911,15 @@ contract FastBridgeV2SrcTest is FastBridgeV2Test { bridge({caller: userA, msgValue: 0, params: tokenParams}); skip(DEADLINE + PERMISSIONLESS_REFUND_DELAY); vm.expectRevert(DeadlineNotExceeded.selector); - refund({caller: caller, bridgeTx: ethTx}); + refund({caller: caller, bridgeTx: tokenTx}); } function test_refund_revert_statusNull() public { - vm.skip(true); // TODO: unskip when fixed vm.expectRevert(StatusIncorrect.selector); refund({caller: refunder, bridgeTx: ethTx}); } function test_refund_revert_statusProven() public { - vm.skip(true); // TODO: unskip when fixed bridge({caller: userA, msgValue: 0, params: tokenParams}); prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); vm.expectRevert(StatusIncorrect.selector); @@ -929,7 +927,6 @@ contract FastBridgeV2SrcTest is FastBridgeV2Test { } function test_refund_revert_statusClaimed() public { - vm.skip(true); // TODO: unskip when fixed bridge({caller: userA, msgValue: 0, params: tokenParams}); prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); skip(CLAIM_DELAY + 1); From 3222ee86b857cfcbfca1d203746ea586b9a11c24 Mon Sep 17 00:00:00 2001 From: parodime Date: Thu, 26 Sep 2024 18:33:37 +0000 Subject: [PATCH 51/90] Publish - @synapsecns/contracts-rfq@0.5.7 --- packages/contracts-rfq/CHANGELOG.md | 8 ++++++++ packages/contracts-rfq/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/contracts-rfq/CHANGELOG.md b/packages/contracts-rfq/CHANGELOG.md index feec5d7356..cfc0a722ff 100644 --- a/packages/contracts-rfq/CHANGELOG.md +++ b/packages/contracts-rfq/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.7](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.6...@synapsecns/contracts-rfq@0.5.7) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/contracts-rfq + + + + + ## [0.5.6](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.5...@synapsecns/contracts-rfq@0.5.6) (2024-09-26) **Note:** Version bump only for package @synapsecns/contracts-rfq diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index b0c33eafd4..a919f6cfb9 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/contracts-rfq", "license": "MIT", - "version": "0.5.6", + "version": "0.5.7", "description": "FastBridge contracts.", "private": true, "files": [ From aa37b503786824d7a2460640be7d2484f0491071 Mon Sep 17 00:00:00 2001 From: abtestingalpha <104046418+abtestingalpha@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:56:24 -0400 Subject: [PATCH 52/90] fix(synapse-interface): Generalizes airdrop decimal display based on SDK gasAirdropAmount [SLT-269] (#3196) * Generalizes airdrop decimal display based on SDK gasAirdropAmount --- .../BridgeExchangeRateInfo.tsx | 18 +- .../utils/getSignificantDecimals.ts | 23 + .../utils/hooks/useCoingeckoPrice.ts | 2 +- yarn.lock | 643 +----------------- 4 files changed, 43 insertions(+), 643 deletions(-) create mode 100644 packages/synapse-interface/utils/getSignificantDecimals.ts diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeExchangeRateInfo.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeExchangeRateInfo.tsx index 52c9e9ffcb..7b59d6e8df 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeExchangeRateInfo.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeExchangeRateInfo.tsx @@ -11,6 +11,7 @@ import { EMPTY_BRIDGE_QUOTE } from '@/constants/bridge' import { CHAINS_BY_ID } from '@constants/chains' import * as CHAINS from '@constants/chains/master' import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks' +import { getSignificantDecimals } from '@/utils/getSignificantDecimals' export const BridgeExchangeRateInfo = () => { /* TODO: @@ -134,7 +135,6 @@ const TimeEstimate = () => { } const GasDropLabel = () => { - let decimalsToDisplay const { toChainId } = useBridgeState() const { bridgeQuote: { gasDropAmount }, @@ -143,20 +143,13 @@ const GasDropLabel = () => { const t = useTranslations('Bridge') const symbol = CHAINS_BY_ID[toChainId]?.nativeCurrency.symbol - if ([CHAINS.FANTOM.id].includes(toChainId)) { - decimalsToDisplay = 2 - } else if ( - [CHAINS.BNB.id, CHAINS.AVALANCHE.id, CHAINS.BOBA.id].includes(toChainId) - ) { - decimalsToDisplay = 3 - } else { - decimalsToDisplay = 4 - } + const stringifiedGasAmount = formatBigIntToString(gasDropAmount, 18) + const significantDecimals = getSignificantDecimals(stringifiedGasAmount) const formattedGasDropAmount = formatBigIntToString( gasDropAmount, 18, - decimalsToDisplay + significantDecimals ) const airdropInDollars = getAirdropInDollars(symbol, formattedGasDropAmount) @@ -214,12 +207,13 @@ const getAirdropInDollars = ( symbol: string, formattedGasDropAmount: string ) => { + const decimals = symbol === 'JEWEL' ? 4 : 2 const price = useCoingeckoPrice(symbol) if (price) { const airdropInDollars = parseFloat(formattedGasDropAmount) * price - return airdropInDollars.toFixed(2) + return airdropInDollars.toFixed(decimals) } else { return undefined } diff --git a/packages/synapse-interface/utils/getSignificantDecimals.ts b/packages/synapse-interface/utils/getSignificantDecimals.ts new file mode 100644 index 0000000000..90e29990e7 --- /dev/null +++ b/packages/synapse-interface/utils/getSignificantDecimals.ts @@ -0,0 +1,23 @@ +export const getSignificantDecimals = ( + numberString: string, + defaultDecimals: number = 2 +): number => { + const parts = numberString.split('.') + const decimalPart = parts[1] + + if (!decimalPart) return 0 + + if (/^0*$/.test(decimalPart)) { + return defaultDecimals + } + + let significantDecimals = 0 + + for (let i = 0; i < decimalPart.length; i++) { + if (decimalPart[i] !== '0') { + significantDecimals = i + 1 + } + } + + return significantDecimals +} diff --git a/packages/synapse-interface/utils/hooks/useCoingeckoPrice.ts b/packages/synapse-interface/utils/hooks/useCoingeckoPrice.ts index 604334008a..7bdfdda905 100644 --- a/packages/synapse-interface/utils/hooks/useCoingeckoPrice.ts +++ b/packages/synapse-interface/utils/hooks/useCoingeckoPrice.ts @@ -8,7 +8,7 @@ const ID_MAP = { GLMR: 'moonbeam', CANTO: 'canto', FTM: 'fantom', - METIS: 'metis-token', + Metis: 'metis-token', BNB: 'binancecoin', MATIC: 'matic-network', KLAY: 'klay-token', diff --git a/yarn.lock b/yarn.lock index 65b9c966b1..826b7331b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1523,7 +1523,7 @@ "@babel/parser" "^7.25.0" "@babel/types" "^7.25.0" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.11.5", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.22.8", "@babel/traverse@^7.23.2", "@babel/traverse@^7.24.7", "@babel/traverse@^7.24.8", "@babel/traverse@^7.25.0", "@babel/traverse@^7.25.1", "@babel/traverse@^7.25.2", "@babel/traverse@^7.25.3", "@babel/traverse@^7.25.4", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2": +"@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.11.5", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.22.8", "@babel/traverse@^7.23.2", "@babel/traverse@^7.24.7", "@babel/traverse@^7.24.8", "@babel/traverse@^7.25.0", "@babel/traverse@^7.25.1", "@babel/traverse@^7.25.2", "@babel/traverse@^7.25.3", "@babel/traverse@^7.25.4", "@babel/traverse@^7.7.0": version "7.25.6" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.6.tgz#04fad980e444f182ecf1520504941940a90fea41" integrity sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ== @@ -4383,18 +4383,6 @@ jest-util "^25.5.0" slash "^3.0.0" -"@jest/console@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-28.1.3.tgz#2030606ec03a18c31803b8a36382762e447655df" - integrity sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw== - dependencies: - "@jest/types" "^28.1.3" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - slash "^3.0.0" - "@jest/console@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" @@ -4441,41 +4429,6 @@ slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/core@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-28.1.3.tgz#0ebf2bd39840f1233cd5f2d1e6fc8b71bd5a1ac7" - integrity sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA== - dependencies: - "@jest/console" "^28.1.3" - "@jest/reporters" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - ci-info "^3.2.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^28.1.3" - jest-config "^28.1.3" - jest-haste-map "^28.1.3" - jest-message-util "^28.1.3" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-resolve-dependencies "^28.1.3" - jest-runner "^28.1.3" - jest-runtime "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" - jest-watcher "^28.1.3" - micromatch "^4.0.4" - pretty-format "^28.1.3" - rimraf "^3.0.0" - slash "^3.0.0" - strip-ansi "^6.0.0" - "@jest/core@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" @@ -4519,16 +4472,6 @@ "@jest/types" "^25.5.0" jest-mock "^25.5.0" -"@jest/environment@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-28.1.3.tgz#abed43a6b040a4c24fdcb69eab1f97589b2d663e" - integrity sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA== - dependencies: - "@jest/fake-timers" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - jest-mock "^28.1.3" - "@jest/environment@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" @@ -4539,13 +4482,6 @@ "@types/node" "*" jest-mock "^29.7.0" -"@jest/expect-utils@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" - integrity sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA== - dependencies: - jest-get-type "^28.0.2" - "@jest/expect-utils@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" @@ -4553,14 +4489,6 @@ dependencies: jest-get-type "^29.6.3" -"@jest/expect@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-28.1.3.tgz#9ac57e1d4491baca550f6bdbd232487177ad6a72" - integrity sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw== - dependencies: - expect "^28.1.3" - jest-snapshot "^28.1.3" - "@jest/expect@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" @@ -4580,18 +4508,6 @@ jest-util "^25.5.0" lolex "^5.0.0" -"@jest/fake-timers@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-28.1.3.tgz#230255b3ad0a3d4978f1d06f70685baea91c640e" - integrity sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw== - dependencies: - "@jest/types" "^28.1.3" - "@sinonjs/fake-timers" "^9.1.2" - "@types/node" "*" - jest-message-util "^28.1.3" - jest-mock "^28.1.3" - jest-util "^28.1.3" - "@jest/fake-timers@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" @@ -4613,15 +4529,6 @@ "@jest/types" "^25.5.0" expect "^25.5.0" -"@jest/globals@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-28.1.3.tgz#a601d78ddc5fdef542728309894895b4a42dc333" - integrity sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA== - dependencies: - "@jest/environment" "^28.1.3" - "@jest/expect" "^28.1.3" - "@jest/types" "^28.1.3" - "@jest/globals@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" @@ -4664,37 +4571,6 @@ optionalDependencies: node-notifier "^6.0.0" -"@jest/reporters@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-28.1.3.tgz#9adf6d265edafc5fc4a434cfb31e2df5a67a369a" - integrity sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@jridgewell/trace-mapping" "^0.3.13" - "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^5.1.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - jest-worker "^28.1.3" - slash "^3.0.0" - string-length "^4.0.1" - strip-ansi "^6.0.0" - terminal-link "^2.0.0" - v8-to-istanbul "^9.0.1" - "@jest/reporters@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" @@ -4725,13 +4601,6 @@ strip-ansi "^6.0.0" v8-to-istanbul "^9.0.1" -"@jest/schemas@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" - integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg== - dependencies: - "@sinclair/typebox" "^0.24.1" - "@jest/schemas@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" @@ -4748,15 +4617,6 @@ graceful-fs "^4.2.4" source-map "^0.6.0" -"@jest/source-map@^28.1.2": - version "28.1.2" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-28.1.2.tgz#7fe832b172b497d6663cdff6c13b0a920e139e24" - integrity sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww== - dependencies: - "@jridgewell/trace-mapping" "^0.3.13" - callsites "^3.0.0" - graceful-fs "^4.2.9" - "@jest/source-map@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" @@ -4776,16 +4636,6 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-result@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.3.tgz#5eae945fd9f4b8fcfce74d239e6f725b6bf076c5" - integrity sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg== - dependencies: - "@jest/console" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - "@jest/test-result@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" @@ -4807,16 +4657,6 @@ jest-runner "^25.5.4" jest-runtime "^25.5.4" -"@jest/test-sequencer@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz#9d0c283d906ac599c74bde464bc0d7e6a82886c3" - integrity sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw== - dependencies: - "@jest/test-result" "^28.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - slash "^3.0.0" - "@jest/test-sequencer@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" @@ -4870,27 +4710,6 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/transform@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-28.1.3.tgz#59d8098e50ab07950e0f2fc0fc7ec462371281b0" - integrity sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA== - dependencies: - "@babel/core" "^7.11.6" - "@jest/types" "^28.1.3" - "@jridgewell/trace-mapping" "^0.3.13" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-regex-util "^28.0.2" - jest-util "^28.1.3" - micromatch "^4.0.4" - pirates "^4.0.4" - slash "^3.0.0" - write-file-atomic "^4.0.1" - "@jest/transform@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" @@ -4953,18 +4772,6 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jest/types@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b" - integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ== - dependencies: - "@jest/schemas" "^28.1.3" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - "@jest/types@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" @@ -5017,7 +4824,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== @@ -7507,11 +7314,6 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== -"@sinclair/typebox@^0.24.1": - version "0.24.51" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" - integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== - "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -7558,13 +7360,6 @@ dependencies: "@sinonjs/commons" "^3.0.0" -"@sinonjs/fake-timers@^9.1.2": - version "9.1.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" - integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== - dependencies: - "@sinonjs/commons" "^1.7.0" - "@slorber/remark-comment@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@slorber/remark-comment/-/remark-comment-1.0.0.tgz#2a020b3f4579c89dec0361673206c28d67e08f5a" @@ -8894,6 +8689,14 @@ "@swc/counter" "^0.1.3" tslib "^2.4.0" +"@synapsecns/coverage-aggregator@file:./packages/coverage-aggregator": + version "1.0.6" + dependencies: + glob "^8.0.3" + path "^0.12.7" + ts-jest "^29.0.5" + yargs "^17.6.2" + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -9778,7 +9581,7 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== -"@types/prettier@^2.1.1", "@types/prettier@^2.1.5": +"@types/prettier@^2.1.1": version "2.7.3" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== @@ -12368,19 +12171,6 @@ babel-jest@^25.2.6, babel-jest@^25.5.1: graceful-fs "^4.2.4" slash "^3.0.0" -babel-jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-28.1.3.tgz#c1187258197c099072156a0a121c11ee1e3917d5" - integrity sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q== - dependencies: - "@jest/transform" "^28.1.3" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^28.1.3" - chalk "^4.0.0" - graceful-fs "^4.2.9" - slash "^3.0.0" - babel-jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" @@ -12485,16 +12275,6 @@ babel-plugin-jest-hoist@^25.5.0: "@babel/types" "^7.3.3" "@types/babel__traverse" "^7.0.6" -babel-plugin-jest-hoist@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz#1952c4d0ea50f2d6d794353762278d1d8cca3fbe" - integrity sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.1.14" - "@types/babel__traverse" "^7.0.6" - babel-plugin-jest-hoist@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" @@ -12690,14 +12470,6 @@ babel-preset-jest@^25.5.0: babel-plugin-jest-hoist "^25.5.0" babel-preset-current-node-syntax "^0.1.2" -babel-preset-jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz#5dfc20b99abed5db994406c2b9ab94c73aaa419d" - integrity sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A== - dependencies: - babel-plugin-jest-hoist "^28.1.3" - babel-preset-current-node-syntax "^1.0.0" - babel-preset-jest@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" @@ -16174,11 +15946,6 @@ diff-sequences@^25.2.6: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg== -diff-sequences@^28.1.1: - version "28.1.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" - integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw== - diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" @@ -16667,11 +16434,6 @@ elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4, elliptic@^6. minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -emittery@^0.10.2: - version "0.10.2" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" - integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== - emittery@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" @@ -18497,17 +18259,6 @@ expect@^25.5.0: jest-message-util "^25.5.0" jest-regex-util "^25.2.6" -expect@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec" - integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g== - dependencies: - "@jest/expect-utils" "^28.1.3" - jest-get-type "^28.0.2" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - expect@^29.0.0, expect@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" @@ -22463,7 +22214,7 @@ istanbul-lib-instrument@^4.0.0: istanbul-lib-coverage "^3.0.0" semver "^6.3.0" -istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: +istanbul-lib-instrument@^5.0.4: version "5.2.1" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== @@ -22583,14 +22334,6 @@ jest-changed-files@^25.5.0: execa "^3.2.0" throat "^5.0.0" -jest-changed-files@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-28.1.3.tgz#d9aeee6792be3686c47cb988a8eaf82ff4238831" - integrity sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA== - dependencies: - execa "^5.0.0" - p-limit "^3.1.0" - jest-changed-files@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" @@ -22600,31 +22343,6 @@ jest-changed-files@^29.7.0: jest-util "^29.7.0" p-limit "^3.1.0" -jest-circus@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-28.1.3.tgz#d14bd11cf8ee1a03d69902dc47b6bd4634ee00e4" - integrity sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow== - dependencies: - "@jest/environment" "^28.1.3" - "@jest/expect" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^0.7.0" - is-generator-fn "^2.0.0" - jest-each "^28.1.3" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-runtime "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" - p-limit "^3.1.0" - pretty-format "^28.1.3" - slash "^3.0.0" - stack-utils "^2.0.3" - jest-circus@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" @@ -22671,24 +22389,6 @@ jest-cli@^25.5.4: realpath-native "^2.0.0" yargs "^15.3.1" -jest-cli@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-28.1.3.tgz#558b33c577d06de55087b8448d373b9f654e46b2" - integrity sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ== - dependencies: - "@jest/core" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - import-local "^3.0.2" - jest-config "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" - prompts "^2.0.1" - yargs "^17.3.1" - jest-cli@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" @@ -22731,34 +22431,6 @@ jest-config@^25.5.4: pretty-format "^25.5.0" realpath-native "^2.0.0" -jest-config@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-28.1.3.tgz#e315e1f73df3cac31447eed8b8740a477392ec60" - integrity sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ== - dependencies: - "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^28.1.3" - "@jest/types" "^28.1.3" - babel-jest "^28.1.3" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-circus "^28.1.3" - jest-environment-node "^28.1.3" - jest-get-type "^28.0.2" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-runner "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" - micromatch "^4.0.4" - parse-json "^5.2.0" - pretty-format "^28.1.3" - slash "^3.0.0" - strip-json-comments "^3.1.1" - jest-config@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" @@ -22807,16 +22479,6 @@ jest-diff@^25.2.1, jest-diff@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" -jest-diff@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f" - integrity sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw== - dependencies: - chalk "^4.0.0" - diff-sequences "^28.1.1" - jest-get-type "^28.0.2" - pretty-format "^28.1.3" - jest-diff@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" @@ -22834,13 +22496,6 @@ jest-docblock@^25.3.0: dependencies: detect-newline "^3.0.0" -jest-docblock@^28.1.1: - version "28.1.1" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-28.1.1.tgz#6f515c3bf841516d82ecd57a62eed9204c2f42a8" - integrity sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA== - dependencies: - detect-newline "^3.0.0" - jest-docblock@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" @@ -22859,17 +22514,6 @@ jest-each@^25.5.0: jest-util "^25.5.0" pretty-format "^25.5.0" -jest-each@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-28.1.3.tgz#bdd1516edbe2b1f3569cfdad9acd543040028f81" - integrity sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g== - dependencies: - "@jest/types" "^28.1.3" - chalk "^4.0.0" - jest-get-type "^28.0.2" - jest-util "^28.1.3" - pretty-format "^28.1.3" - jest-each@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" @@ -22905,18 +22549,6 @@ jest-environment-node@^25.5.0: jest-util "^25.5.0" semver "^6.3.0" -jest-environment-node@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-28.1.3.tgz#7e74fe40eb645b9d56c0c4b70ca4357faa349be5" - integrity sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A== - dependencies: - "@jest/environment" "^28.1.3" - "@jest/fake-timers" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - jest-mock "^28.1.3" - jest-util "^28.1.3" - jest-environment-node@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" @@ -22947,11 +22579,6 @@ jest-get-type@^25.2.6: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig== -jest-get-type@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" - integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== - jest-get-type@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" @@ -22998,25 +22625,6 @@ jest-haste-map@^26.6.2: optionalDependencies: fsevents "^2.1.2" -jest-haste-map@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-28.1.3.tgz#abd5451129a38d9841049644f34b034308944e2b" - integrity sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA== - dependencies: - "@jest/types" "^28.1.3" - "@types/graceful-fs" "^4.1.3" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.9" - jest-regex-util "^28.0.2" - jest-util "^28.1.3" - jest-worker "^28.1.3" - micromatch "^4.0.4" - walker "^1.0.8" - optionalDependencies: - fsevents "^2.3.2" - jest-haste-map@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" @@ -23067,14 +22675,6 @@ jest-leak-detector@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" -jest-leak-detector@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz#a6685d9b074be99e3adee816ce84fd30795e654d" - integrity sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA== - dependencies: - jest-get-type "^28.0.2" - pretty-format "^28.1.3" - jest-leak-detector@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" @@ -23093,16 +22693,6 @@ jest-matcher-utils@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" -jest-matcher-utils@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e" - integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw== - dependencies: - chalk "^4.0.0" - jest-diff "^28.1.3" - jest-get-type "^28.0.2" - pretty-format "^28.1.3" - jest-matcher-utils@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" @@ -23127,21 +22717,6 @@ jest-message-util@^25.5.0: slash "^3.0.0" stack-utils "^1.0.1" -jest-message-util@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d" - integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^28.1.3" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^28.1.3" - slash "^3.0.0" - stack-utils "^2.0.3" - jest-message-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" @@ -23179,14 +22754,6 @@ jest-mock@^27.0.6: "@jest/types" "^27.5.1" "@types/node" "*" -jest-mock@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-28.1.3.tgz#d4e9b1fc838bea595c77ab73672ebf513ab249da" - integrity sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA== - dependencies: - "@jest/types" "^28.1.3" - "@types/node" "*" - jest-mock@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" @@ -23211,11 +22778,6 @@ jest-regex-util@^26.0.0: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== -jest-regex-util@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead" - integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw== - jest-regex-util@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" @@ -23230,14 +22792,6 @@ jest-resolve-dependencies@^25.5.4: jest-regex-util "^25.2.6" jest-snapshot "^25.5.1" -jest-resolve-dependencies@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz#8c65d7583460df7275c6ea2791901fa975c1fe66" - integrity sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA== - dependencies: - jest-regex-util "^28.0.2" - jest-snapshot "^28.1.3" - jest-resolve-dependencies@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" @@ -23261,21 +22815,6 @@ jest-resolve@^25.5.1: resolve "^1.17.0" slash "^3.0.0" -jest-resolve@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-28.1.3.tgz#cfb36100341ddbb061ec781426b3c31eb51aa0a8" - integrity sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ== - dependencies: - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-pnp-resolver "^1.2.2" - jest-util "^28.1.3" - jest-validate "^28.1.3" - resolve "^1.20.0" - resolve.exports "^1.1.0" - slash "^3.0.0" - jest-resolve@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" @@ -23316,33 +22855,6 @@ jest-runner@^25.5.4: source-map-support "^0.5.6" throat "^5.0.0" -jest-runner@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-28.1.3.tgz#5eee25febd730b4713a2cdfd76bdd5557840f9a1" - integrity sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA== - dependencies: - "@jest/console" "^28.1.3" - "@jest/environment" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.10.2" - graceful-fs "^4.2.9" - jest-docblock "^28.1.1" - jest-environment-node "^28.1.3" - jest-haste-map "^28.1.3" - jest-leak-detector "^28.1.3" - jest-message-util "^28.1.3" - jest-resolve "^28.1.3" - jest-runtime "^28.1.3" - jest-util "^28.1.3" - jest-watcher "^28.1.3" - jest-worker "^28.1.3" - p-limit "^3.1.0" - source-map-support "0.5.13" - jest-runner@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" @@ -23402,34 +22914,6 @@ jest-runtime@^25.5.4: strip-bom "^4.0.0" yargs "^15.3.1" -jest-runtime@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-28.1.3.tgz#a57643458235aa53e8ec7821949e728960d0605f" - integrity sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw== - dependencies: - "@jest/environment" "^28.1.3" - "@jest/fake-timers" "^28.1.3" - "@jest/globals" "^28.1.3" - "@jest/source-map" "^28.1.2" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - execa "^5.0.0" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-message-util "^28.1.3" - jest-mock "^28.1.3" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" - slash "^3.0.0" - strip-bom "^4.0.0" - jest-runtime@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" @@ -23494,35 +22978,6 @@ jest-snapshot@^25.5.1: pretty-format "^25.5.0" semver "^6.3.0" -jest-snapshot@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-28.1.3.tgz#17467b3ab8ddb81e2f605db05583d69388fc0668" - integrity sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg== - dependencies: - "@babel/core" "^7.11.6" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" - "@babel/types" "^7.3.3" - "@jest/expect-utils" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/babel__traverse" "^7.0.6" - "@types/prettier" "^2.1.5" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^28.1.3" - graceful-fs "^4.2.9" - jest-diff "^28.1.3" - jest-get-type "^28.0.2" - jest-haste-map "^28.1.3" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - natural-compare "^1.4.0" - pretty-format "^28.1.3" - semver "^7.3.5" - jest-snapshot@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" @@ -23572,18 +23027,6 @@ jest-util@^26.6.2: is-ci "^2.0.0" micromatch "^4.0.2" -jest-util@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" - integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ== - dependencies: - "@jest/types" "^28.1.3" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - jest-util@^29.0.0, jest-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" @@ -23608,18 +23051,6 @@ jest-validate@^25.5.0: leven "^3.1.0" pretty-format "^25.5.0" -jest-validate@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-28.1.3.tgz#e322267fd5e7c64cea4629612c357bbda96229df" - integrity sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA== - dependencies: - "@jest/types" "^28.1.3" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^28.0.2" - leven "^3.1.0" - pretty-format "^28.1.3" - jest-validate@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" @@ -23657,20 +23088,6 @@ jest-watcher@^25.2.4, jest-watcher@^25.5.0: jest-util "^25.5.0" string-length "^3.1.0" -jest-watcher@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-28.1.3.tgz#c6023a59ba2255e3b4c57179fc94164b3e73abd4" - integrity sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g== - dependencies: - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - emittery "^0.10.2" - jest-util "^28.1.3" - string-length "^4.0.1" - jest-watcher@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" @@ -23719,15 +23136,6 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.3.tgz#7e3c4ce3fa23d1bb6accb169e7f396f98ed4bb98" - integrity sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^8.0.0" - jest-worker@^29.1.2, jest-worker@^29.4.3, jest-worker@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" @@ -23747,16 +23155,6 @@ jest@^25.3.0: import-local "^3.0.2" jest-cli "^25.5.4" -jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest/-/jest-28.1.3.tgz#e9c6a7eecdebe3548ca2b18894a50f45b36dfc6b" - integrity sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA== - dependencies: - "@jest/core" "^28.1.3" - "@jest/types" "^28.1.3" - import-local "^3.0.2" - jest-cli "^28.1.3" - jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" @@ -29948,16 +29346,6 @@ pretty-format@^27.0.2: ansi-styles "^5.0.0" react-is "^17.0.1" -pretty-format@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" - integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q== - dependencies: - "@jest/schemas" "^28.1.3" - ansi-regex "^5.0.1" - ansi-styles "^5.0.0" - react-is "^18.0.0" - pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" @@ -31746,11 +31134,6 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== -resolve.exports@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" - integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== - resolve.exports@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" @@ -37993,7 +37376,7 @@ write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write-file-atomic@^4.0.1, write-file-atomic@^4.0.2: +write-file-atomic@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== From 257e57c702c97ec6314390930609725e023848c2 Mon Sep 17 00:00:00 2001 From: abtestingalpha Date: Thu, 26 Sep 2024 19:00:25 +0000 Subject: [PATCH 53/90] Publish - @synapsecns/synapse-interface@0.40.1 --- packages/synapse-interface/CHANGELOG.md | 11 +++++++++++ packages/synapse-interface/package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/synapse-interface/CHANGELOG.md b/packages/synapse-interface/CHANGELOG.md index 8af7d86ed2..110099bb71 100644 --- a/packages/synapse-interface/CHANGELOG.md +++ b/packages/synapse-interface/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.40.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.40.0...@synapsecns/synapse-interface@0.40.1) (2024-09-26) + + +### Bug Fixes + +* **synapse-interface:** Generalizes airdrop decimal display based on SDK gasAirdropAmount [SLT-269] ([#3196](https://github.com/synapsecns/sanguine/issues/3196)) ([aa37b50](https://github.com/synapsecns/sanguine/commit/aa37b503786824d7a2460640be7d2484f0491071)) + + + + + # [0.40.0](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.39.2...@synapsecns/synapse-interface@0.40.0) (2024-09-26) diff --git a/packages/synapse-interface/package.json b/packages/synapse-interface/package.json index 586401902a..ea81ff218a 100644 --- a/packages/synapse-interface/package.json +++ b/packages/synapse-interface/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/synapse-interface", - "version": "0.40.0", + "version": "0.40.1", "private": true, "engines": { "node": ">=18.18.0" From 21de254f6e1676cad1422e9999550ff351413618 Mon Sep 17 00:00:00 2001 From: Moses <103143573+Defi-Moses@users.noreply.github.com> Date: Thu, 26 Sep 2024 21:51:39 +0100 Subject: [PATCH 54/90] REST API ReadME (#3183) * fixing api readme * small nits * formatting * final formatting --- packages/rest-api/README.md | 114 +++++++++++++++++------------------- 1 file changed, 55 insertions(+), 59 deletions(-) diff --git a/packages/rest-api/README.md b/packages/rest-api/README.md index baab635b4b..3b83d587dd 100644 --- a/packages/rest-api/README.md +++ b/packages/rest-api/README.md @@ -1,81 +1,77 @@ -# Swap/Bridge REST API Quoter +# Synapse REST API +To make requests, use https://api.synapseprotocol.com/ -To run locally: -\`npm start\` - -To make requests, use https://synapse-rest-api-v2.herokuapp.com/ -The Synapse Rest API supports four main functions +To run locally: +```bash +yarn dev +``` -## /swap +# Documentation +[Swagger Documentation](https://api.synapseprotocol.com/api-docs/) -which returns the following +[GitBook Documentation](https://docs.synapseprotocol.com/developers/rest-api) -- \`routerAddress\` (string) - The address of the router contract -- \`maxAmountOut\` (object) - The maximum amount of tokens that can be swapped out. Contains: - - \`type\` (string) - The data type - - \`hex\` (string) - The amount encoded in hexidecimal -- \`query\` (object) - Parameters for the swap query: - - \`0\` (string) - Router contract address - - \`1\` (string) - Address of tokenIn - - \`2\` (object) - Amount of tokenIn to swap (same structure as maxAmountOut) - - \`3\` (object) - Minimum amount of tokenOut requested (same structure as maxAmountOut) - - \`4\` (string) - Encoded params for swap routing - - \`swapAdapter\` (string) - Address of the swap adapter contract - - \`tokenOut\` (string) - Address of tokenOut - - \`minAmountOut\` (object) - Minimum amount of tokenOut required (same structure as maxAmountOut) - - \`deadline\` (object) - Deadline parameter for the swap (same structure as maxAmountOut) - - \`rawParams\` (string) - Encoded hex string containing swap parameters -- \`maxAmountOutStr\` (string) - The maxAmountOut value formatted as a decimal string +## REST API +The Synapse REST API provides a set of endpoints for quoting and executing cross-chain token swaps and bridges. It supports various functions including swap quotes, bridge quotes, transaction information retrieval, and token list management. -All \`/swap\` requests should be formatted like such: +## Formatting Requests +Here's an example of how to format a bridge request: -\`/swap?chain=1&fromToken=USDC&toToken=DAI&amount=100\` +```bash +/bridge?fromChain=1&toChain=42161&fromToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&toToken=0xaf88d065e77c8cC2239327C5EDb3A432268e5831&amount=100 +``` -## /bridge +To use this in a basic application: -which returns all transaction information +```javascript +async function getBridgeQuote() { + const response = await fetch('https://api.synapseprotocol.com/bridge?fromChain=1&toChain=42161&fromToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&toToken=0xaf88d065e77c8cC2239327C5EDb3A432268e5831&amount=100'); + const data = await response.json(); + console.log(data); +} +getBridgeQuote(); +``` -- \`feeAmount\` (object) - The fee amount for the swap. Contains: - - \`type\` (string) - Data type - - \`hex\` (string) - Fee amount encoded in hex -- \`feeConfig\` (array) - Fee configuration parameters, contains: - - \`0\` (number) - Gas price - - \`1\` (object) - Fee percentage denominator (hex encoded BigNumber) - - \`2\` (object) - Protocol fee percentage numerator (hex encoded BigNumber) -- \`routerAddress\` (string) - Address of the router contract -- \`maxAmountOut\` (object) - Maximum amount receivable from swap, structure same as above -- \`originQuery\` (object) - Original swap query parameters, contains: - - \`swapAdapter\` (string) - Swap adapter address - - \`tokenOut\` (string) - Address of output token - - \`minAmountOut\` (object) - Minimum output token amount - - \`deadline\` (object) - Expiry time - - \`rawParams\` (string) - Encoded hex params -- \`destQuery\` (object) - Destination swap query parameters, structure similar to originQuery above. -- \`maxAmountOutStr\` (string) - maxAmountOut as a decimal string. -All \`/bridge\` requests should be formatted like such: +## Bridging -\`/bridge?fromChain=1&toChain=42161&fromToken=USDC&toToken=USDC&amount=1000000\` +1. Get a Bridge Quote to confirm the expected amount out and the bridge route -## /swapTxInfo +```javascript +const quoteResponse = await fetch('https://api.synapseprotocol.com/bridge?fromChain=1&toChain=42161&fromToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&toToken=0xaf88d065e77c8cC2239327C5EDb3A432268e5831&amount=100'); +const quoteData = await quoteResponse.json(); +``` -which returns the following +2. Get the structured transaction information -- \`'data'\`: The binary data that forms the input to the transaction. -- \`'to'\`: The address of the Synapse Router (the synapse bridge contract) +```javascript +const txInfoResponse = await fetch('https://api.synapseprotocol.com/bridgeTxInfo?fromChain=1&toChain=42161&fromToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&toToken=0xaf88d065e77c8cC2239327C5EDb3A432268e5831&amount=100&destAddress=0xcc78d2f004c9de9694ff6a9bbdee4793d30f3842'); +const txInfoData = await txInfoResponse.json(); +``` -All \`/swapTxInfo\` requests should be formatted like such: +3. Execute the transaction -\`/swap?chain=1&fromToken=USDC&toToken=DAI&amount=100\` +```javascript +const provider = new ethers.providers.Web3Provider(window.ethereum); +const signer = provider.getSigner(); +const transaction = await signer.sendTransaction(txInfoData); +const receipt = await transaction.wait(); +``` -## /bridgeTxInfo +## Other Functions: +1. `/destinationTokens`: This endpoint provides information about possible destination tokens for a given source chain and token. It's useful for showing users their options when initiating a cross-chain transfer. -which returns the following +```javascript +const response = await fetch('https://api.synapseprotocol.com/destinationTokens?fromChain=1&fromToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'); +const destinationTokens = await response.json(); +``` -- \`'data'\`: The binary data that forms the input to the transaction. -- \`'to'\`: The address of the Synapse Router (the synapse bridge contract) +2. `/bridgeLimits`: This endpoint returns the minimum and maximum amounts that can be bridged for a specific token pair. It's helpful for validating user input and displaying available limits. -All \`/bridgeTxInfo\` requests should be formatted like such: +```javascript +const limitsResponse = await fetch('https://api.synapseprotocol.com/bridgeLimits?fromChain=1&toChain=42161&fromToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&toToken=0xaf88d065e77c8cC2239327C5EDb3A432268e5831'); +const limitsData = await limitsResponse.json(); +``` -\`/bridgeTxInfo?fromChain=1&toChain=42161&fromToken=USDC&toToken=USDC&amount=1000000&destAddress=0xcc78d2f004c9de9694ff6a9bbdee4793d30f3842\` +There are other additional functions that are included and documented in the Swagger documentation. Suggested changes to the API can be made by creating a new branch, making the changes, and opening a pull request. Any changes should include the appropriate test coverage and update the relevant documentation (this README, Swagger, and GitBook). From edf99d3a5f515f77b965d6505afbd834f0386fb4 Mon Sep 17 00:00:00 2001 From: Defi-Moses Date: Thu, 26 Sep 2024 20:55:46 +0000 Subject: [PATCH 55/90] Publish - @synapsecns/rest-api@1.3.3 --- packages/rest-api/CHANGELOG.md | 8 ++++++++ packages/rest-api/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/rest-api/CHANGELOG.md b/packages/rest-api/CHANGELOG.md index fab320e869..d7ee944825 100644 --- a/packages/rest-api/CHANGELOG.md +++ b/packages/rest-api/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.3.3](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.3.2...@synapsecns/rest-api@1.3.3) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/rest-api + + + + + ## [1.3.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.3.1...@synapsecns/rest-api@1.3.2) (2024-09-26) **Note:** Version bump only for package @synapsecns/rest-api diff --git a/packages/rest-api/package.json b/packages/rest-api/package.json index 5b6c7f8c1b..14e8e92496 100644 --- a/packages/rest-api/package.json +++ b/packages/rest-api/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/rest-api", - "version": "1.3.2", + "version": "1.3.3", "private": "true", "engines": { "node": ">=18.17.0" From 0a573d1c1855f1682dbc1dc7722934593c2a70bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CF=87=C2=B2?= <88190723+ChiTimesChi@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:54:24 +0100 Subject: [PATCH 56/90] fix(contracts-rfq): gas estimation tests [SLT-275] (#3204) * refactor: isolate common utils for SRC tests * test: gas benchmark for SRC actions * test: gas benchmark for DST actions * test: rework bench tests with mroe isolation * fix: set non-zero initial balances for asset receivers * ci: use gas bench tests only --- .github/workflows/solidity.yml | 9 +- .../test/FastBridgeV2.Dst.Base.t.sol | 50 ++++ .../contracts-rfq/test/FastBridgeV2.Dst.t.sol | 41 +--- .../test/FastBridgeV2.GasBench.Dst.t.sol | 65 +++++ .../FastBridgeV2.GasBench.Src.PFees.t.sol | 36 +++ .../test/FastBridgeV2.GasBench.Src.t.sol | 223 ++++++++++++++++++ .../test/FastBridgeV2.Src.Base.t.sol | 89 +++++++ .../contracts-rfq/test/FastBridgeV2.Src.t.sol | 84 +------ 8 files changed, 469 insertions(+), 128 deletions(-) create mode 100644 packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol create mode 100644 packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.t.sol create mode 100644 packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.PFees.t.sol create mode 100644 packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol create mode 100644 packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol diff --git a/.github/workflows/solidity.yml b/.github/workflows/solidity.yml index ee44b8d940..19d0d643a1 100644 --- a/.github/workflows/solidity.yml +++ b/.github/workflows/solidity.yml @@ -264,15 +264,10 @@ jobs: with: version: nightly - # TODO: consider defining a package-specific script for this - name: Run tests and generate gas report working-directory: './packages/${{matrix.package}}' - # Excluding tests with reverts to get accurate average gas cost estimates - run: forge test --nmt "(fail|revert)" --gas-report > "../../gas-report-${{ matrix.package }}.ansi" - env: - # make fuzzing semi-deterministic to avoid noisy gas cost estimation - # due to non-deterministic fuzzing (but still use pseudo-random fuzzing seeds) - FOUNDRY_FUZZ_SEED: 0x${{ github.event.pull_request.base.sha || github.sha }} + # Run separate set of tests (no fuzzing) to get accurate average gas cost estimates + run: forge test --mc GasBenchmark --gas-report > "../../gas-report-${{ matrix.package }}.ansi" - name: Compare gas reports uses: Rubilmax/foundry-gas-diff@v3.18 diff --git a/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol new file mode 100644 index 0000000000..bf113a2078 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2, FastBridgeV2Test, IFastBridge} from "./FastBridgeV2.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2DstBaseTest is FastBridgeV2Test { + uint256 public constant LEFTOVER_BALANCE = 1 ether; + + function setUp() public override { + vm.chainId(DST_CHAIN_ID); + super.setUp(); + } + + function deployFastBridge() public override returns (FastBridgeV2) { + return new FastBridgeV2(address(this)); + } + + function mintTokens() public virtual override { + dstToken.mint(address(relayerA), LEFTOVER_BALANCE + tokenParams.destAmount); + dstToken.mint(address(relayerB), LEFTOVER_BALANCE + tokenParams.destAmount); + deal(relayerA, LEFTOVER_BALANCE + ethParams.destAmount); + deal(relayerB, LEFTOVER_BALANCE + ethParams.destAmount); + vm.prank(relayerA); + dstToken.approve(address(fastBridge), type(uint256).max); + vm.prank(relayerB); + dstToken.approve(address(fastBridge), type(uint256).max); + } + + // ══════════════════════════════════════════════════ HELPERS ══════════════════════════════════════════════════════ + + function relay(address caller, uint256 msgValue, IFastBridge.BridgeTransaction memory bridgeTx) public { + bytes memory request = abi.encode(bridgeTx); + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.relay{value: msgValue}(request); + } + + function relayWithAddress( + address caller, + address relayer, + uint256 msgValue, + IFastBridge.BridgeTransaction memory bridgeTx + ) + public + { + bytes memory request = abi.encode(bridgeTx); + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.relay{value: msgValue}(request, relayer); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol index e4848d53dc..3389c1690a 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol @@ -3,10 +3,10 @@ pragma solidity ^0.8.20; import {ChainIncorrect, DeadlineExceeded, TransactionRelayed, ZeroAddress} from "../contracts/libs/Errors.sol"; -import {FastBridgeV2, FastBridgeV2Test, IFastBridge} from "./FastBridgeV2.t.sol"; +import {FastBridgeV2DstBaseTest, IFastBridge} from "./FastBridgeV2.Dst.Base.t.sol"; // solhint-disable func-name-mixedcase, ordering -contract FastBridgeV2DstTest is FastBridgeV2Test { +contract FastBridgeV2DstTest is FastBridgeV2DstBaseTest { event BridgeRelayed( bytes32 indexed transactionId, address indexed relayer, @@ -19,24 +19,6 @@ contract FastBridgeV2DstTest is FastBridgeV2Test { uint256 chainGasAmount ); - uint256 public constant LEFTOVER_BALANCE = 1 ether; - - function setUp() public override { - vm.chainId(DST_CHAIN_ID); - super.setUp(); - } - - function deployFastBridge() public override returns (FastBridgeV2) { - return new FastBridgeV2(address(this)); - } - - function mintTokens() public override { - dstToken.mint(address(relayerA), LEFTOVER_BALANCE + tokenParams.destAmount); - deal(relayerB, LEFTOVER_BALANCE + ethParams.destAmount); - vm.prank(relayerA); - dstToken.approve(address(fastBridge), type(uint256).max); - } - function expectBridgeRelayed(IFastBridge.BridgeTransaction memory bridgeTx, bytes32 txId, address relayer) public { vm.expectEmit(address(fastBridge)); emit BridgeRelayed({ @@ -52,25 +34,6 @@ contract FastBridgeV2DstTest is FastBridgeV2Test { }); } - function relay(address caller, uint256 msgValue, IFastBridge.BridgeTransaction memory bridgeTx) public { - bytes memory request = abi.encode(bridgeTx); - vm.prank(caller); - fastBridge.relay{value: msgValue}(request); - } - - function relayWithAddress( - address caller, - address relayer, - uint256 msgValue, - IFastBridge.BridgeTransaction memory bridgeTx - ) - public - { - bytes memory request = abi.encode(bridgeTx); - vm.prank(caller); - fastBridge.relay{value: msgValue}(request, relayer); - } - /// @notice RelayerA completes the ERC20 bridge request function test_relay_token() public { bytes32 txId = getTxId(tokenTx); diff --git a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.t.sol new file mode 100644 index 0000000000..6afabce02e --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.t.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2DstBaseTest} from "./FastBridgeV2.Dst.Base.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +/// @notice This test is used to estimate the gas cost of FastBridgeV2 destination chain functions. +/// Very little state checks are performed, make sure to do full coverage in different tests. +contract FastBridgeV2DstGasBenchmarkTest is FastBridgeV2DstBaseTest { + uint256 public constant INITIAL_USER_BALANCE = 100 ether; + + function mintTokens() public virtual override { + super.mintTokens(); + deal(userB, INITIAL_USER_BALANCE); + dstToken.mint(userB, INITIAL_USER_BALANCE); + } + + // ═══════════════════════════════════════════════════ TOKEN ═══════════════════════════════════════════════════════ + + function test_relay_token() public { + bytes32 txId = getTxId(tokenTx); + relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx}); + (uint256 blockNumber, uint256 blockTimestamp, address relayer) = fastBridge.bridgeRelayDetails(txId); + assertEq(blockNumber, block.number); + assertEq(blockTimestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(dstToken.balanceOf(userB), INITIAL_USER_BALANCE + tokenParams.destAmount); + assertEq(dstToken.balanceOf(relayerA), LEFTOVER_BALANCE); + } + + function test_relay_token_withRelayerAddress() public { + bytes32 txId = getTxId(tokenTx); + relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: 0, bridgeTx: tokenTx}); + (uint256 blockNumber, uint256 blockTimestamp, address relayer) = fastBridge.bridgeRelayDetails(txId); + assertEq(blockNumber, block.number); + assertEq(blockTimestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(dstToken.balanceOf(userB), INITIAL_USER_BALANCE + tokenParams.destAmount); + assertEq(dstToken.balanceOf(relayerB), LEFTOVER_BALANCE); + } + + // ════════════════════════════════════════════════════ ETH ════════════════════════════════════════════════════════ + + function test_relay_eth() public { + bytes32 txId = getTxId(ethTx); + relay({caller: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + (uint256 blockNumber, uint256 blockTimestamp, address relayer) = fastBridge.bridgeRelayDetails(txId); + assertEq(blockNumber, block.number); + assertEq(blockTimestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(address(userB).balance, INITIAL_USER_BALANCE + ethParams.destAmount); + assertEq(address(relayerA).balance, LEFTOVER_BALANCE); + } + + function test_relay_eth_withRelayerAddress() public { + bytes32 txId = getTxId(ethTx); + relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + (uint256 blockNumber, uint256 blockTimestamp, address relayer) = fastBridge.bridgeRelayDetails(txId); + assertEq(blockNumber, block.number); + assertEq(blockTimestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(address(userB).balance, INITIAL_USER_BALANCE + ethParams.destAmount); + assertEq(address(relayerB).balance, LEFTOVER_BALANCE); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.PFees.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.PFees.t.sol new file mode 100644 index 0000000000..77b439d18e --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.PFees.t.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2GasBenchmarkSrcTest} from "./FastBridgeV2.GasBench.Src.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2GasBenchmarkSrcProtocolFeesTest is FastBridgeV2GasBenchmarkSrcTest { + function configureFastBridge() public virtual override { + super.configureFastBridge(); + fastBridge.grantRole(fastBridge.GOVERNOR_ROLE(), address(this)); + fastBridge.setProtocolFeeRate(1e4); // 1% + } + + function createFixtures() public virtual override { + super.createFixtures(); + tokenTx.originFeeAmount = 0.01e6; + tokenTx.originAmount = 0.99e6; + tokenTx.destAmount = 0.98e6; + tokenParams.destAmount = 0.98e6; + ethTx.originFeeAmount = 0.01 ether; + ethTx.originAmount = 0.99 ether; + ethTx.destAmount = 0.98 ether; + ethParams.destAmount = 0.98 ether; + + // Copy txs to bridged and proven with different nonce + bridgedTokenTx = tokenTx; + provenTokenTx = tokenTx; + bridgedEthTx = ethTx; + provenEthTx = ethTx; + + bridgedTokenTx.nonce = 0; + bridgedEthTx.nonce = 1; + provenTokenTx.nonce = 2; + provenEthTx.nonce = 3; + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol new file mode 100644 index 0000000000..424fdd5e2e --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2, FastBridgeV2SrcBaseTest, IFastBridge} from "./FastBridgeV2.Src.Base.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +/// @notice This test is used to estimate the gas cost of FastBridgeV2 source chain functions. +/// Very little state checks are performed, make sure to do full coverage in different tests. +contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { + uint256 public constant BLOCK_TIME = 12 seconds; + uint256 public constant INITIAL_RELAYER_BALANCE = 100 ether; + + IFastBridge.BridgeTransaction public bridgedTokenTx; + IFastBridge.BridgeTransaction public bridgedEthTx; + + IFastBridge.BridgeTransaction public provenTokenTx; + IFastBridge.BridgeTransaction public provenEthTx; + + uint256 public initialUserBalanceToken; + uint256 public initialUserBalanceEth; + uint256 public initialFastBridgeBalanceToken; + uint256 public initialFastBridgeBalanceEth; + + function setUp() public virtual override { + super.setUp(); + initExistingTxs(); + initialUserBalanceToken = srcToken.balanceOf(userA); + initialUserBalanceEth = userA.balance; + initialFastBridgeBalanceToken = srcToken.balanceOf(address(fastBridge)); + initialFastBridgeBalanceEth = address(fastBridge).balance; + } + + function createFixtures() public virtual override { + super.createFixtures(); + bridgedTokenTx = tokenTx; + provenTokenTx = tokenTx; + bridgedEthTx = ethTx; + provenEthTx = ethTx; + + bridgedTokenTx.nonce = 0; + bridgedEthTx.nonce = 1; + provenTokenTx.nonce = 2; + provenEthTx.nonce = 3; + // Next nonce for userA tx would be 4 (either token or eth) + tokenTx.nonce = 4; + ethTx.nonce = 4; + } + + function mintTokens() public virtual override { + super.mintTokens(); + srcToken.mint(relayerA, INITIAL_RELAYER_BALANCE); + srcToken.mint(relayerB, INITIAL_RELAYER_BALANCE); + deal(relayerA, INITIAL_RELAYER_BALANCE); + deal(relayerB, INITIAL_RELAYER_BALANCE); + } + + function initExistingTxs() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + skipBlocksExactly(1); + prove({caller: relayerA, bridgeTx: provenTokenTx, destTxHash: hex"01"}); + prove({caller: relayerB, transactionId: getTxId(provenEthTx), destTxHash: hex"02", relayer: relayerA}); + // Status checks + assertEq(fastBridge.bridgeStatuses(getTxId(bridgedTokenTx)), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(getTxId(bridgedEthTx)), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(getTxId(provenTokenTx)), FastBridgeV2.BridgeStatus.RELAYER_PROVED); + assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), FastBridgeV2.BridgeStatus.RELAYER_PROVED); + assertEq(fastBridge.bridgeStatuses(getTxId(tokenTx)), FastBridgeV2.BridgeStatus.NULL); + assertEq(fastBridge.bridgeStatuses(getTxId(ethTx)), FastBridgeV2.BridgeStatus.NULL); + } + + function skipBlocksExactly(uint256 blocks) public { + vm.roll(block.number + blocks); + vm.warp(block.timestamp + blocks * BLOCK_TIME); + } + + function skipTimeAtLeast(uint256 time) public { + uint256 blocksToSkip = time / BLOCK_TIME; + if (blocksToSkip * BLOCK_TIME < time) blocksToSkip++; + skipBlocksExactly(blocksToSkip); + } + + // ═══════════════════════════════════════════════════ TOKEN ═══════════════════════════════════════════════════════ + + function test_bridge_token() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + assertEq(fastBridge.bridgeStatuses(getTxId(tokenTx)), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(srcToken.balanceOf(userA), initialUserBalanceToken - tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken + tokenParams.originAmount); + } + + function test_prove_token() public { + bytes32 txId = getTxId(bridgedTokenTx); + prove({caller: relayerA, bridgeTx: bridgedTokenTx, destTxHash: hex"03"}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_PROVED); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken); + } + + function test_proveWithAddress_token() public { + bytes32 txId = getTxId(bridgedTokenTx); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"03", relayer: relayerA}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_PROVED); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + } + + function test_claim_token() public { + skipTimeAtLeast({time: CLAIM_DELAY + 1}); + claim({caller: relayerA, bridgeTx: provenTokenTx}); + assertEq(fastBridge.bridgeStatuses(getTxId(provenTokenTx)), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(srcToken.balanceOf(relayerA), INITIAL_RELAYER_BALANCE + tokenTx.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenTx.originAmount); + } + + function test_claimWithAddress_token() public { + skipTimeAtLeast({time: CLAIM_DELAY + 1}); + claim({caller: relayerA, bridgeTx: provenTokenTx, to: relayerB}); + assertEq(fastBridge.bridgeStatuses(getTxId(provenTokenTx)), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(srcToken.balanceOf(relayerB), INITIAL_RELAYER_BALANCE + tokenTx.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenTx.originAmount); + } + + function test_dispute_token() public { + bytes32 txId = getTxId(provenTokenTx); + dispute({caller: guard, txId: txId}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken); + } + + function test_refundPermissioned_token() public { + bytes32 txId = getTxId(bridgedTokenTx); + skipTimeAtLeast({time: DEADLINE}); + refund({caller: refunder, bridgeTx: bridgedTokenTx}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(srcToken.balanceOf(userA), initialUserBalanceToken + tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenParams.originAmount); + } + + function test_refundPermissionless_token() public { + bytes32 txId = getTxId(bridgedTokenTx); + skipTimeAtLeast({time: DEADLINE + PERMISSIONLESS_REFUND_DELAY}); + refund({caller: userB, bridgeTx: bridgedTokenTx}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(srcToken.balanceOf(userA), initialUserBalanceToken + tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenParams.originAmount); + } + + // ════════════════════════════════════════════════════ ETH ════════════════════════════════════════════════════════ + + function test_bridge_eth() public { + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + assertEq(fastBridge.bridgeStatuses(getTxId(ethTx)), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(userA.balance, initialUserBalanceEth - ethParams.originAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth + ethParams.originAmount); + } + + function test_prove_eth() public { + bytes32 txId = getTxId(bridgedEthTx); + prove({caller: relayerA, bridgeTx: bridgedEthTx, destTxHash: hex"03"}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_PROVED); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth); + } + + function test_proveWithAddress_eth() public { + bytes32 txId = getTxId(bridgedEthTx); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"03", relayer: relayerA}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_PROVED); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth); + } + + function test_claim_eth() public { + skipTimeAtLeast({time: CLAIM_DELAY + 1}); + claim({caller: relayerA, bridgeTx: provenEthTx}); + assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(relayerA.balance, INITIAL_RELAYER_BALANCE + ethTx.originAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethTx.originAmount); + } + + function test_claimWithAddress_eth() public { + skipTimeAtLeast({time: CLAIM_DELAY + 1}); + claim({caller: relayerA, bridgeTx: provenEthTx, to: relayerB}); + assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(relayerB.balance, INITIAL_RELAYER_BALANCE + ethTx.originAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethTx.originAmount); + } + + function test_dispute_eth() public { + bytes32 txId = getTxId(provenEthTx); + dispute({caller: guard, txId: txId}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth); + } + + function test_refundPermissioned_eth() public { + bytes32 txId = getTxId(bridgedEthTx); + skipTimeAtLeast({time: DEADLINE}); + refund({caller: refunder, bridgeTx: bridgedEthTx}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(userA.balance, initialUserBalanceEth + ethParams.originAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethParams.originAmount); + } + + function test_refundPermissionless_eth() public { + bytes32 txId = getTxId(bridgedEthTx); + skipTimeAtLeast({time: DEADLINE + PERMISSIONLESS_REFUND_DELAY}); + refund({caller: userB, bridgeTx: bridgedEthTx}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(userA.balance, initialUserBalanceEth + ethParams.originAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethParams.originAmount); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol new file mode 100644 index 0000000000..2ce33eca6f --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2, FastBridgeV2Test, IFastBridge} from "./FastBridgeV2.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +abstract contract FastBridgeV2SrcBaseTest is FastBridgeV2Test { + uint256 public constant MIN_DEADLINE = 30 minutes; + uint256 public constant CLAIM_DELAY = 30 minutes; + uint256 public constant PERMISSIONLESS_REFUND_DELAY = 7 days; + + uint256 public constant LEFTOVER_BALANCE = 10 ether; + uint256 public constant INITIAL_PROTOCOL_FEES_TOKEN = 456_789; + uint256 public constant INITIAL_PROTOCOL_FEES_ETH = 0.123 ether; + + function setUp() public virtual override { + vm.chainId(SRC_CHAIN_ID); + super.setUp(); + } + + function deployFastBridge() public virtual override returns (FastBridgeV2) { + return new FastBridgeV2(address(this)); + } + + function configureFastBridge() public virtual override { + fastBridge.grantRole(fastBridge.RELAYER_ROLE(), relayerA); + fastBridge.grantRole(fastBridge.RELAYER_ROLE(), relayerB); + fastBridge.grantRole(fastBridge.GUARD_ROLE(), guard); + fastBridge.grantRole(fastBridge.REFUNDER_ROLE(), refunder); + } + + function mintTokens() public virtual override { + // Prior Protocol fees + srcToken.mint(address(fastBridge), INITIAL_PROTOCOL_FEES_TOKEN); + deal(address(fastBridge), INITIAL_PROTOCOL_FEES_ETH); + cheatCollectedProtocolFees(address(srcToken), INITIAL_PROTOCOL_FEES_TOKEN); + cheatCollectedProtocolFees(ETH_ADDRESS, INITIAL_PROTOCOL_FEES_ETH); + // Users + srcToken.mint(userA, LEFTOVER_BALANCE + tokenParams.originAmount); + srcToken.mint(userB, LEFTOVER_BALANCE + tokenParams.originAmount); + deal(userA, LEFTOVER_BALANCE + ethParams.originAmount); + deal(userB, LEFTOVER_BALANCE + ethParams.originAmount); + vm.prank(userA); + srcToken.approve(address(fastBridge), type(uint256).max); + vm.prank(userB); + srcToken.approve(address(fastBridge), type(uint256).max); + } + + // ══════════════════════════════════════════════════ HELPERS ══════════════════════════════════════════════════════ + + function bridge(address caller, uint256 msgValue, IFastBridge.BridgeParams memory params) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.bridge{value: msgValue}(params); + } + + function prove(address caller, bytes32 transactionId, bytes32 destTxHash, address relayer) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.prove(transactionId, destTxHash, relayer); + } + + function prove(address caller, IFastBridge.BridgeTransaction memory bridgeTx, bytes32 destTxHash) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.prove(abi.encode(bridgeTx), destTxHash); + } + + function claim(address caller, IFastBridge.BridgeTransaction memory bridgeTx) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.claim(abi.encode(bridgeTx)); + } + + function claim(address caller, IFastBridge.BridgeTransaction memory bridgeTx, address to) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.claim(abi.encode(bridgeTx), to); + } + + function dispute(address caller, bytes32 txId) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.dispute(txId); + } + + function refund(address caller, IFastBridge.BridgeTransaction memory bridgeTx) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.refund(abi.encode(bridgeTx)); + } + + function assertEq(FastBridgeV2.BridgeStatus a, FastBridgeV2.BridgeStatus b) public pure { + assertEq(uint8(a), uint8(b)); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol index d1336885e3..593722ab1d 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {FastBridgeV2, FastBridgeV2Test, IFastBridge} from "./FastBridgeV2.t.sol"; +import {FastBridgeV2, FastBridgeV2SrcBaseTest, IFastBridge} from "./FastBridgeV2.Src.Base.t.sol"; // solhint-disable func-name-mixedcase, ordering -contract FastBridgeV2SrcTest is FastBridgeV2Test { +contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { event BridgeRequested( bytes32 indexed transactionId, address indexed sender, @@ -27,84 +27,8 @@ contract FastBridgeV2SrcTest is FastBridgeV2Test { event BridgeDepositRefunded(bytes32 indexed transactionId, address indexed to, address token, uint256 amount); - uint256 public constant MIN_DEADLINE = 30 minutes; - uint256 public constant CLAIM_DELAY = 30 minutes; - uint256 public constant PERMISSIONLESS_REFUND_DELAY = 7 days; - - uint256 public constant LEFTOVER_BALANCE = 1 ether; - uint256 public constant INITIAL_PROTOCOL_FEES_TOKEN = 456_789; - uint256 public constant INITIAL_PROTOCOL_FEES_ETH = 0.123 ether; - address public claimTo = makeAddr("Claim To"); - function setUp() public override { - vm.chainId(SRC_CHAIN_ID); - super.setUp(); - } - - function deployFastBridge() public override returns (FastBridgeV2) { - return new FastBridgeV2(address(this)); - } - - function configureFastBridge() public virtual override { - fastBridge.grantRole(fastBridge.RELAYER_ROLE(), relayerA); - fastBridge.grantRole(fastBridge.RELAYER_ROLE(), relayerB); - fastBridge.grantRole(fastBridge.GUARD_ROLE(), guard); - fastBridge.grantRole(fastBridge.REFUNDER_ROLE(), refunder); - } - - function mintTokens() public override { - // Prior Protocol fees - srcToken.mint(address(fastBridge), INITIAL_PROTOCOL_FEES_TOKEN); - deal(address(fastBridge), INITIAL_PROTOCOL_FEES_ETH); - cheatCollectedProtocolFees(address(srcToken), INITIAL_PROTOCOL_FEES_TOKEN); - cheatCollectedProtocolFees(ETH_ADDRESS, INITIAL_PROTOCOL_FEES_ETH); - // Users - srcToken.mint(userA, LEFTOVER_BALANCE + tokenParams.originAmount); - srcToken.mint(userB, LEFTOVER_BALANCE + tokenParams.originAmount); - deal(userA, LEFTOVER_BALANCE + ethParams.originAmount); - deal(userB, LEFTOVER_BALANCE + ethParams.originAmount); - vm.prank(userA); - srcToken.approve(address(fastBridge), type(uint256).max); - vm.prank(userB); - srcToken.approve(address(fastBridge), type(uint256).max); - } - - function bridge(address caller, uint256 msgValue, IFastBridge.BridgeParams memory params) public { - vm.prank(caller); - fastBridge.bridge{value: msgValue}(params); - } - - function prove(address caller, bytes32 transactionId, bytes32 destTxHash, address relayer) public { - vm.prank(caller); - fastBridge.prove(transactionId, destTxHash, relayer); - } - - function prove(address caller, IFastBridge.BridgeTransaction memory bridgeTx, bytes32 destTxHash) public { - vm.prank(caller); - fastBridge.prove(abi.encode(bridgeTx), destTxHash); - } - - function claim(address caller, IFastBridge.BridgeTransaction memory bridgeTx) public { - vm.prank(caller); - fastBridge.claim(abi.encode(bridgeTx)); - } - - function claim(address caller, IFastBridge.BridgeTransaction memory bridgeTx, address to) public { - vm.prank(caller); - fastBridge.claim(abi.encode(bridgeTx), to); - } - - function dispute(address caller, bytes32 txId) public { - vm.prank(caller); - fastBridge.dispute(txId); - } - - function refund(address caller, IFastBridge.BridgeTransaction memory bridgeTx) public { - vm.prank(caller); - fastBridge.refund(abi.encode(bridgeTx)); - } - function expectBridgeRequested(IFastBridge.BridgeTransaction memory bridgeTx, bytes32 txId) public { vm.expectEmit(address(fastBridge)); emit BridgeRequested({ @@ -159,10 +83,6 @@ contract FastBridgeV2SrcTest is FastBridgeV2Test { }); } - function assertEq(FastBridgeV2.BridgeStatus a, FastBridgeV2.BridgeStatus b) public pure { - assertEq(uint8(a), uint8(b)); - } - // ══════════════════════════════════════════════════ BRIDGE ═══════════════════════════════════════════════════════ function checkTokenBalancesAfterBridge(address caller) public view { From 5fd9bacaddc9203cd9156f48130b5d79b2c79598 Mon Sep 17 00:00:00 2001 From: ChiTimesChi Date: Fri, 27 Sep 2024 14:58:36 +0000 Subject: [PATCH 57/90] Publish - @synapsecns/contracts-rfq@0.5.8 --- packages/contracts-rfq/CHANGELOG.md | 11 +++++++++++ packages/contracts-rfq/package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/contracts-rfq/CHANGELOG.md b/packages/contracts-rfq/CHANGELOG.md index cfc0a722ff..6306e5875e 100644 --- a/packages/contracts-rfq/CHANGELOG.md +++ b/packages/contracts-rfq/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.8](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.7...@synapsecns/contracts-rfq@0.5.8) (2024-09-27) + + +### Bug Fixes + +* **contracts-rfq:** gas estimation tests [SLT-275] ([#3204](https://github.com/synapsecns/sanguine/issues/3204)) ([0a573d1](https://github.com/synapsecns/sanguine/commit/0a573d1c1855f1682dbc1dc7722934593c2a70bd)) + + + + + ## [0.5.7](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.6...@synapsecns/contracts-rfq@0.5.7) (2024-09-26) **Note:** Version bump only for package @synapsecns/contracts-rfq diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index a919f6cfb9..54d6f8d6f1 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/contracts-rfq", "license": "MIT", - "version": "0.5.7", + "version": "0.5.8", "description": "FastBridge contracts.", "private": true, "files": [ From 0240714b0c7bcaca40574c430239ea3e1b7927f7 Mon Sep 17 00:00:00 2001 From: parodime Date: Fri, 27 Sep 2024 19:47:50 -0400 Subject: [PATCH 58/90] status/proof mapping packing [SLT-186] (#3173) * status/proof mapping packing [SLT-186] * fix BridgeProofs def to use struct * WIP - evaluating ProofDetail struct approach * fix: overflow in parity tests * refactor BridgeTxDetails w/o addtl ProofDetail struct * master merge conflicts * restore bad lint warning * switch to fully qualified bridgeTxDetails instead of storage refs * rearrange for lint (120 char line) * test fixes * code cleanup --------- Co-authored-by: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Co-authored-by: jw --- .../contracts-rfq/contracts/FastBridgeV2.sol | 85 ++++++++++--------- .../contracts/interfaces/IFastBridgeV2.sol | 26 ++++++ packages/contracts-rfq/test/FastBridge.t.sol | 34 ++++---- .../test/FastBridgeV2.GasBench.Src.t.sol | 47 +++++----- .../test/FastBridgeV2.Parity.t.sol | 15 ++++ .../contracts-rfq/test/FastBridgeV2.Src.t.sol | 57 +++++++------ .../contracts-rfq/test/FastBridgeV2.t.sol | 2 + 7 files changed, 159 insertions(+), 107 deletions(-) diff --git a/packages/contracts-rfq/contracts/FastBridgeV2.sol b/packages/contracts-rfq/contracts/FastBridgeV2.sol index a92c0746f0..e649ab0ec0 100644 --- a/packages/contracts-rfq/contracts/FastBridgeV2.sol +++ b/packages/contracts-rfq/contracts/FastBridgeV2.sol @@ -24,18 +24,8 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { /// @notice Minimum deadline period to relay a requested bridge transaction uint256 public constant MIN_DEADLINE_PERIOD = 30 minutes; - enum BridgeStatus { - NULL, // doesn't exist yet - REQUESTED, - RELAYER_PROVED, - RELAYER_CLAIMED, - REFUNDED - } - /// @notice Status of the bridge tx on origin chain - mapping(bytes32 => BridgeStatus) public bridgeStatuses; - /// @notice Proof of relayed bridge tx on origin chain - mapping(bytes32 => BridgeProof) public bridgeProofs; + mapping(bytes32 => BridgeTxDetails) public bridgeTxDetails; /// @notice Relay details on destination chain mapping(bytes32 => BridgeRelay) public bridgeRelayDetails; @@ -44,6 +34,15 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { // @dev the block the contract was deployed at uint256 public immutable deployBlock; + function bridgeStatuses(bytes32 transactionId) public view returns (BridgeStatus status) { + return bridgeTxDetails[transactionId].status; + } + + function bridgeProofs(bytes32 transactionId) public view returns (uint96 timestamp, address relayer) { + timestamp = bridgeTxDetails[transactionId].proofBlockTimestamp; + relayer = bridgeTxDetails[transactionId].proofRelayer; + } + constructor(address _owner) Admin(_owner) { deployBlock = block.number; } @@ -111,7 +110,7 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { }) ); bytes32 transactionId = keccak256(request); - bridgeStatuses[transactionId] = BridgeStatus.REQUESTED; + bridgeTxDetails[transactionId].status = BridgeStatus.REQUESTED; emit BridgeRequested( transactionId, @@ -194,32 +193,32 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { /// @inheritdoc IFastBridgeV2 function prove(bytes32 transactionId, bytes32 destTxHash, address relayer) public onlyRole(RELAYER_ROLE) { // update bridge tx status given proof provided - if (bridgeStatuses[transactionId] != BridgeStatus.REQUESTED) revert StatusIncorrect(); - bridgeStatuses[transactionId] = BridgeStatus.RELAYER_PROVED; - // overflow ok - bridgeProofs[transactionId] = BridgeProof({timestamp: uint96(block.timestamp), relayer: relayer}); + if (bridgeTxDetails[transactionId].status != BridgeStatus.REQUESTED) revert StatusIncorrect(); + bridgeTxDetails[transactionId].status = BridgeStatus.RELAYER_PROVED; + bridgeTxDetails[transactionId].proofBlockTimestamp = uint40(block.timestamp); + bridgeTxDetails[transactionId].proofBlockNumber = uint48(block.number); + bridgeTxDetails[transactionId].proofRelayer = relayer; emit BridgeProofProvided(transactionId, relayer, destTxHash); } /// @notice Calculates time since proof submitted - /// @dev proof.timestamp stores casted uint96(block.timestamp) block timestamps for gas optimization - /// _timeSince(proof) can accomodate rollover case when block.timestamp > type(uint96).max but - /// proof.timestamp < type(uint96).max via unchecked statement - /// @param proof The bridge proof + /// @dev proof.timestamp stores casted uint40(block.timestamp) block timestamps for gas optimization + /// _timeSince(proof) can accomodate rollover case when block.timestamp > type(uint40).max but + /// proof.timestamp < type(uint40).max via unchecked statement + /// @param proofBlockTimestamp The bridge proof block timestamp /// @return delta Time delta since proof submitted - function _timeSince(BridgeProof memory proof) internal view returns (uint256 delta) { + function _timeSince(uint40 proofBlockTimestamp) internal view returns (uint256 delta) { unchecked { - delta = uint96(block.timestamp) - proof.timestamp; + delta = uint40(block.timestamp) - proofBlockTimestamp; } } /// @inheritdoc IFastBridge function canClaim(bytes32 transactionId, address relayer) external view returns (bool) { - if (bridgeStatuses[transactionId] != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect(); - BridgeProof memory proof = bridgeProofs[transactionId]; - if (proof.relayer != relayer) revert SenderIncorrect(); - return _timeSince(proof) > DISPUTE_PERIOD; + if (bridgeTxDetails[transactionId].status != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect(); + if (bridgeTxDetails[transactionId].proofRelayer != relayer) revert SenderIncorrect(); + return _timeSince(bridgeTxDetails[transactionId].proofBlockTimestamp) > DISPUTE_PERIOD; } /// @inheritdoc IFastBridgeV2 @@ -233,20 +232,20 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { BridgeTransaction memory transaction = getBridgeTransaction(request); // update bridge tx status if able to claim origin collateral - if (bridgeStatuses[transactionId] != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect(); - - BridgeProof memory proof = bridgeProofs[transactionId]; + if (bridgeTxDetails[transactionId].status != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect(); // if "to" is zero addr, permissionlessly send funds to proven relayer if (to == address(0)) { - to = proof.relayer; - } else if (proof.relayer != msg.sender) { + to = bridgeTxDetails[transactionId].proofRelayer; + } else if (bridgeTxDetails[transactionId].proofRelayer != msg.sender) { revert SenderIncorrect(); } - if (_timeSince(proof) <= DISPUTE_PERIOD) revert DisputePeriodNotPassed(); + if (_timeSince(bridgeTxDetails[transactionId].proofBlockTimestamp) <= DISPUTE_PERIOD) { + revert DisputePeriodNotPassed(); + } - bridgeStatuses[transactionId] = BridgeStatus.RELAYER_CLAIMED; + bridgeTxDetails[transactionId].status = BridgeStatus.RELAYER_CLAIMED; // update protocol fees if origin fee amount exists if (transaction.originFeeAmount > 0) protocolFees[transaction.originToken] += transaction.originFeeAmount; @@ -256,17 +255,21 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { uint256 amount = transaction.originAmount; token.universalTransfer(to, amount); - emit BridgeDepositClaimed(transactionId, proof.relayer, to, token, amount); + emit BridgeDepositClaimed(transactionId, bridgeTxDetails[transactionId].proofRelayer, to, token, amount); } /// @inheritdoc IFastBridge function dispute(bytes32 transactionId) external onlyRole(GUARD_ROLE) { - if (bridgeStatuses[transactionId] != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect(); - if (_timeSince(bridgeProofs[transactionId]) > DISPUTE_PERIOD) revert DisputePeriodPassed(); + if (bridgeTxDetails[transactionId].status != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect(); + if (_timeSince(bridgeTxDetails[transactionId].proofBlockTimestamp) > DISPUTE_PERIOD) { + revert DisputePeriodPassed(); + } // @dev relayer gets slashed effectively if dest relay has gone thru - bridgeStatuses[transactionId] = BridgeStatus.REQUESTED; - delete bridgeProofs[transactionId]; + bridgeTxDetails[transactionId].status = BridgeStatus.REQUESTED; + bridgeTxDetails[transactionId].proofRelayer = address(0); + bridgeTxDetails[transactionId].proofBlockTimestamp = 0; + bridgeTxDetails[transactionId].proofBlockNumber = 0; emit BridgeProofDisputed(transactionId, msg.sender); } @@ -275,10 +278,10 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { function refund(bytes memory request) external { bytes32 transactionId = keccak256(request); - if (bridgeStatuses[transactionId] != BridgeStatus.REQUESTED) revert StatusIncorrect(); - BridgeTransaction memory transaction = getBridgeTransaction(request); + if (bridgeTxDetails[transactionId].status != BridgeStatus.REQUESTED) revert StatusIncorrect(); + if (hasRole(REFUNDER_ROLE, msg.sender)) { // Refunder can refund if deadline has passed if (block.timestamp <= transaction.deadline) revert DeadlineNotExceeded(); @@ -288,7 +291,7 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { } // if all checks passed, set to REFUNDED status - bridgeStatuses[transactionId] = BridgeStatus.REFUNDED; + bridgeTxDetails[transactionId].status = BridgeStatus.REFUNDED; // transfer origin collateral back to original sender address to = transaction.originSender; diff --git a/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol index cec58048f8..35c6173224 100644 --- a/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol +++ b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol @@ -4,6 +4,21 @@ pragma solidity ^0.8.20; import {IFastBridge} from "./IFastBridge.sol"; interface IFastBridgeV2 is IFastBridge { + enum BridgeStatus { + NULL, // doesn't exist yet + REQUESTED, + RELAYER_PROVED, + RELAYER_CLAIMED, + REFUNDED + } + + struct BridgeTxDetails { + BridgeStatus status; + uint40 proofBlockTimestamp; + uint48 proofBlockNumber; + address proofRelayer; + } + struct BridgeRelay { uint48 blockNumber; uint48 blockTimestamp; @@ -29,4 +44,15 @@ interface IFastBridgeV2 is IFastBridge { /// @param transactionId The ID of the transaction to check /// @return True if the transaction has been relayed, false otherwise function bridgeRelays(bytes32 transactionId) external view returns (bool); + + /// @notice Returns the status of a bridge transaction + /// @param transactionId The ID of the bridge transaction + /// @return BridgeStatus Status of the bridge transaction + function bridgeStatuses(bytes32 transactionId) external view returns (BridgeStatus); + + /// @notice Returns the timestamp and relayer of a bridge proof + /// @param transactionId The ID of the bridge transaction + /// @return timestamp The timestamp of the bridge proof + /// @return relayer The relayer address of the bridge proof + function bridgeProofs(bytes32 transactionId) external view returns (uint96 timestamp, address relayer); } diff --git a/packages/contracts-rfq/test/FastBridge.t.sol b/packages/contracts-rfq/test/FastBridge.t.sol index 2d90779c96..a571f50b40 100644 --- a/packages/contracts-rfq/test/FastBridge.t.sol +++ b/packages/contracts-rfq/test/FastBridge.t.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.13; +// solhint-disable import "forge-std/Test.sol"; import "forge-std/console2.sol"; @@ -51,6 +52,19 @@ contract FastBridgeTest is Test { ethUSDC.mint(dstUser, 100 * 10 ** 6); } + function assertCorrectProof( + bytes32 transactionId, + uint256 expectedTimestamp, + address expectedRelayer + ) + internal + virtual + { + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(transactionId); + assertEq(timestamp, uint96(expectedTimestamp)); + assertEq(relayer, expectedRelayer); + } + function _getBridgeRequestAndId( uint256 chainId, uint256 currentNonce, @@ -1349,9 +1363,7 @@ contract FastBridgeTest is Test { fastBridge.prove(request, fakeTxnHash); // We check if the bridge transaction proof timestamp is set to the timestamp at which the proof was provided - (uint96 _timestamp, address _oldRelayer) = fastBridge.bridgeProofs(transactionId); - assertEq(_timestamp, uint96(block.timestamp)); - assertEq(_oldRelayer, relayer); + assertCorrectProof(transactionId, block.timestamp, relayer); // We check if the bridge status is RELAYER_PROVED assertEq(uint256(fastBridge.bridgeStatuses(transactionId)), uint256(FastBridge.BridgeStatus.RELAYER_PROVED)); @@ -1383,9 +1395,7 @@ contract FastBridgeTest is Test { fastBridge.prove(request, fakeTxnHash); // We check if the bridge transaction proof timestamp is set to the timestamp at which the proof was provided - (uint96 _timestamp, address _oldRelayer) = fastBridge.bridgeProofs(transactionId); - assertEq(_timestamp, uint96(block.timestamp)); - assertEq(_oldRelayer, relayer); + assertCorrectProof(transactionId, block.timestamp, relayer); // We stop a prank to contain within test vm.stopPrank(); @@ -1414,9 +1424,7 @@ contract FastBridgeTest is Test { fastBridge.prove(request, fakeTxnHash); // We check if the bridge transaction proof timestamp is set to the timestamp at which the proof was provided - (uint96 _timestamp, address _oldRelayer) = fastBridge.bridgeProofs(transactionId); - assertEq(_timestamp, uint96(block.timestamp)); - assertEq(_oldRelayer, relayer); + assertCorrectProof(transactionId, block.timestamp, relayer); // We stop a prank to contain within test vm.stopPrank(); @@ -1713,10 +1721,8 @@ contract FastBridgeTest is Test { fastBridge.dispute(transactionId); // check status and proofs updated - (uint96 _timestamp, address _oldRelayer) = fastBridge.bridgeProofs(transactionId); assertEq(uint256(fastBridge.bridgeStatuses(transactionId)), uint256(FastBridge.BridgeStatus.REQUESTED)); - assertEq(_timestamp, 0); - assertEq(_oldRelayer, address(0)); + assertCorrectProof(transactionId, 0, address(0)); // We stop a prank to contain within test vm.stopPrank(); @@ -1739,10 +1745,8 @@ contract FastBridgeTest is Test { fastBridge.dispute(transactionId); // check status and proofs updated - (uint96 _timestamp, address _oldRelayer) = fastBridge.bridgeProofs(transactionId); assertEq(uint256(fastBridge.bridgeStatuses(transactionId)), uint256(FastBridge.BridgeStatus.REQUESTED)); - assertEq(_timestamp, 0); - assertEq(_oldRelayer, address(0)); + assertCorrectProof(transactionId, 0, address(0)); // We stop a prank to contain within test vm.stopPrank(); diff --git a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol index 424fdd5e2e..42c0cbf7e2 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {FastBridgeV2, FastBridgeV2SrcBaseTest, IFastBridge} from "./FastBridgeV2.Src.Base.t.sol"; +import {FastBridgeV2SrcBaseTest, IFastBridge} from "./FastBridgeV2.Src.Base.t.sol"; +import {IFastBridgeV2} from "../contracts/interfaces/IFastBridgeV2.sol"; // solhint-disable func-name-mixedcase, ordering /// @notice This test is used to estimate the gas cost of FastBridgeV2 source chain functions. @@ -63,12 +64,12 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { prove({caller: relayerA, bridgeTx: provenTokenTx, destTxHash: hex"01"}); prove({caller: relayerB, transactionId: getTxId(provenEthTx), destTxHash: hex"02", relayer: relayerA}); // Status checks - assertEq(fastBridge.bridgeStatuses(getTxId(bridgedTokenTx)), FastBridgeV2.BridgeStatus.REQUESTED); - assertEq(fastBridge.bridgeStatuses(getTxId(bridgedEthTx)), FastBridgeV2.BridgeStatus.REQUESTED); - assertEq(fastBridge.bridgeStatuses(getTxId(provenTokenTx)), FastBridgeV2.BridgeStatus.RELAYER_PROVED); - assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), FastBridgeV2.BridgeStatus.RELAYER_PROVED); - assertEq(fastBridge.bridgeStatuses(getTxId(tokenTx)), FastBridgeV2.BridgeStatus.NULL); - assertEq(fastBridge.bridgeStatuses(getTxId(ethTx)), FastBridgeV2.BridgeStatus.NULL); + assertEq(fastBridge.bridgeStatuses(getTxId(bridgedTokenTx)), IFastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(getTxId(bridgedEthTx)), IFastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(getTxId(provenTokenTx)), IFastBridgeV2.BridgeStatus.RELAYER_PROVED); + assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), IFastBridgeV2.BridgeStatus.RELAYER_PROVED); + assertEq(fastBridge.bridgeStatuses(getTxId(tokenTx)), IFastBridgeV2.BridgeStatus.NULL); + assertEq(fastBridge.bridgeStatuses(getTxId(ethTx)), IFastBridgeV2.BridgeStatus.NULL); } function skipBlocksExactly(uint256 blocks) public { @@ -86,7 +87,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { function test_bridge_token() public { bridge({caller: userA, msgValue: 0, params: tokenParams}); - assertEq(fastBridge.bridgeStatuses(getTxId(tokenTx)), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(getTxId(tokenTx)), IFastBridgeV2.BridgeStatus.REQUESTED); assertEq(srcToken.balanceOf(userA), initialUserBalanceToken - tokenParams.originAmount); assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken + tokenParams.originAmount); } @@ -94,7 +95,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { function test_prove_token() public { bytes32 txId = getTxId(bridgedTokenTx); prove({caller: relayerA, bridgeTx: bridgedTokenTx, destTxHash: hex"03"}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_PROVED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_PROVED); (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); assertEq(timestamp, block.timestamp); assertEq(relayer, relayerA); @@ -104,7 +105,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { function test_proveWithAddress_token() public { bytes32 txId = getTxId(bridgedTokenTx); prove({caller: relayerB, transactionId: txId, destTxHash: hex"03", relayer: relayerA}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_PROVED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_PROVED); (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); assertEq(timestamp, block.timestamp); assertEq(relayer, relayerA); @@ -113,7 +114,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { function test_claim_token() public { skipTimeAtLeast({time: CLAIM_DELAY + 1}); claim({caller: relayerA, bridgeTx: provenTokenTx}); - assertEq(fastBridge.bridgeStatuses(getTxId(provenTokenTx)), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(fastBridge.bridgeStatuses(getTxId(provenTokenTx)), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); assertEq(srcToken.balanceOf(relayerA), INITIAL_RELAYER_BALANCE + tokenTx.originAmount); assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenTx.originAmount); } @@ -121,7 +122,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { function test_claimWithAddress_token() public { skipTimeAtLeast({time: CLAIM_DELAY + 1}); claim({caller: relayerA, bridgeTx: provenTokenTx, to: relayerB}); - assertEq(fastBridge.bridgeStatuses(getTxId(provenTokenTx)), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(fastBridge.bridgeStatuses(getTxId(provenTokenTx)), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); assertEq(srcToken.balanceOf(relayerB), INITIAL_RELAYER_BALANCE + tokenTx.originAmount); assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenTx.originAmount); } @@ -129,7 +130,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { function test_dispute_token() public { bytes32 txId = getTxId(provenTokenTx); dispute({caller: guard, txId: txId}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken); } @@ -137,7 +138,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { bytes32 txId = getTxId(bridgedTokenTx); skipTimeAtLeast({time: DEADLINE}); refund({caller: refunder, bridgeTx: bridgedTokenTx}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); assertEq(srcToken.balanceOf(userA), initialUserBalanceToken + tokenParams.originAmount); assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenParams.originAmount); } @@ -146,7 +147,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { bytes32 txId = getTxId(bridgedTokenTx); skipTimeAtLeast({time: DEADLINE + PERMISSIONLESS_REFUND_DELAY}); refund({caller: userB, bridgeTx: bridgedTokenTx}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); assertEq(srcToken.balanceOf(userA), initialUserBalanceToken + tokenParams.originAmount); assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenParams.originAmount); } @@ -155,7 +156,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { function test_bridge_eth() public { bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); - assertEq(fastBridge.bridgeStatuses(getTxId(ethTx)), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(getTxId(ethTx)), IFastBridgeV2.BridgeStatus.REQUESTED); assertEq(userA.balance, initialUserBalanceEth - ethParams.originAmount); assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth + ethParams.originAmount); } @@ -163,7 +164,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { function test_prove_eth() public { bytes32 txId = getTxId(bridgedEthTx); prove({caller: relayerA, bridgeTx: bridgedEthTx, destTxHash: hex"03"}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_PROVED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_PROVED); (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); assertEq(timestamp, block.timestamp); assertEq(relayer, relayerA); @@ -173,7 +174,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { function test_proveWithAddress_eth() public { bytes32 txId = getTxId(bridgedEthTx); prove({caller: relayerB, transactionId: txId, destTxHash: hex"03", relayer: relayerA}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_PROVED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_PROVED); (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); assertEq(timestamp, block.timestamp); assertEq(relayer, relayerA); @@ -183,7 +184,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { function test_claim_eth() public { skipTimeAtLeast({time: CLAIM_DELAY + 1}); claim({caller: relayerA, bridgeTx: provenEthTx}); - assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); assertEq(relayerA.balance, INITIAL_RELAYER_BALANCE + ethTx.originAmount); assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethTx.originAmount); } @@ -191,7 +192,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { function test_claimWithAddress_eth() public { skipTimeAtLeast({time: CLAIM_DELAY + 1}); claim({caller: relayerA, bridgeTx: provenEthTx, to: relayerB}); - assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); assertEq(relayerB.balance, INITIAL_RELAYER_BALANCE + ethTx.originAmount); assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethTx.originAmount); } @@ -199,7 +200,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { function test_dispute_eth() public { bytes32 txId = getTxId(provenEthTx); dispute({caller: guard, txId: txId}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth); } @@ -207,7 +208,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { bytes32 txId = getTxId(bridgedEthTx); skipTimeAtLeast({time: DEADLINE}); refund({caller: refunder, bridgeTx: bridgedEthTx}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); assertEq(userA.balance, initialUserBalanceEth + ethParams.originAmount); assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethParams.originAmount); } @@ -216,7 +217,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { bytes32 txId = getTxId(bridgedEthTx); skipTimeAtLeast({time: DEADLINE + PERMISSIONLESS_REFUND_DELAY}); refund({caller: userB, bridgeTx: bridgedEthTx}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); assertEq(userA.balance, initialUserBalanceEth + ethParams.originAmount); assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethParams.originAmount); } diff --git a/packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol index dd4b3a4fa2..9990c371d5 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol @@ -14,6 +14,21 @@ contract FastBridgeV2ParityTest is FastBridgeTest, IFastBridgeV2Errors { return deployCode({what: "FastBridgeV2", args: abi.encode(owner)}); } + /// @notice We use uint40 for the timestamps in FastBridgeV2 + function assertCorrectProof( + bytes32 transactionId, + uint256 expectedTimestamp, + address expectedRelayer + ) + internal + virtual + override + { + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(transactionId); + assertEq(timestamp, uint40(expectedTimestamp)); + assertEq(relayer, expectedRelayer); + } + /// @notice Relay function is no longer permissioned, so we skip this test function test_failedRelayNotRelayer() public virtual override { vm.skip(true); diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol index 593722ab1d..e0eb6d6ca3 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {FastBridgeV2, FastBridgeV2SrcBaseTest, IFastBridge} from "./FastBridgeV2.Src.Base.t.sol"; +import {FastBridgeV2SrcBaseTest} from "./FastBridgeV2.Src.Base.t.sol"; +import {IFastBridge, IFastBridgeV2} from "./FastBridgeV2.t.sol"; // solhint-disable func-name-mixedcase, ordering contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { @@ -95,7 +96,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { bytes32 txId = getTxId(tokenTx); expectBridgeRequested(tokenTx, txId); bridge({caller: userA, msgValue: 0, params: tokenParams}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); checkTokenBalancesAfterBridge(userA); } @@ -103,7 +104,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { bytes32 txId = getTxId(tokenTx); expectBridgeRequested(tokenTx, txId); bridge({caller: userB, msgValue: 0, params: tokenParams}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + tokenParams.originAmount); checkTokenBalancesAfterBridge(userB); } @@ -120,7 +121,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { bytes32 txId = getTxId(ethTx); expectBridgeRequested(ethTx, txId); bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); checkEthBalancesAfterBridge(userA); } @@ -130,7 +131,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { bytes32 txId = getTxId(ethTx); expectBridgeRequested(ethTx, txId); bridge({caller: userB, msgValue: ethParams.originAmount, params: ethParams}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); assertEq(userA.balance, LEFTOVER_BALANCE + ethParams.originAmount); checkEthBalancesAfterBridge(userB); } @@ -145,7 +146,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { bytes32 txId = getTxId(ethTx); expectBridgeRequested(ethTx, txId); bridge({caller: userB, msgValue: ethParams.originAmount, params: ethParams}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); checkEthBalancesAfterBridge(userB); } @@ -417,7 +418,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { assertTrue(fastBridge.canClaim(txId, relayerA)); expectBridgeDepositClaimed({bridgeTx: tokenTx, txId: txId, relayer: relayerA, to: relayerA}); claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); checkTokenBalancesAfterClaim(relayerA); } @@ -429,7 +430,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { skip(CLAIM_DELAY + 1); expectBridgeDepositClaimed({bridgeTx: tokenTx, txId: txId, relayer: relayerA, to: relayerA}); claim({caller: caller, bridgeTx: tokenTx}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); checkTokenBalancesAfterClaim(relayerA); } @@ -441,7 +442,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { skip(CLAIM_DELAY + 1); expectBridgeDepositClaimed({bridgeTx: tokenTx, txId: txId, relayer: relayerA, to: relayerA}); claim({caller: caller, bridgeTx: tokenTx, to: address(0)}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); checkTokenBalancesAfterClaim(relayerA); } @@ -452,7 +453,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { skip(CLAIM_DELAY + 1); expectBridgeDepositClaimed({bridgeTx: tokenTx, txId: txId, relayer: relayerA, to: claimTo}); claim({caller: relayerA, bridgeTx: tokenTx, to: claimTo}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); assertEq(srcToken.balanceOf(relayerA), 0); checkTokenBalancesAfterClaim(claimTo); } @@ -464,7 +465,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { skip(CLAIM_DELAY + 30 days); expectBridgeDepositClaimed({bridgeTx: tokenTx, txId: txId, relayer: relayerA, to: relayerA}); claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); checkTokenBalancesAfterClaim(relayerA); } @@ -483,7 +484,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { assertTrue(fastBridge.canClaim(txId, relayerA)); expectBridgeDepositClaimed({bridgeTx: ethTx, txId: txId, relayer: relayerA, to: relayerA}); claim({caller: relayerA, bridgeTx: ethTx, to: relayerA}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); checkEthBalancesAfterClaim(relayerA); } @@ -496,7 +497,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { skip(CLAIM_DELAY + 1); expectBridgeDepositClaimed({bridgeTx: ethTx, txId: txId, relayer: relayerA, to: relayerA}); claim({caller: caller, bridgeTx: ethTx}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); checkEthBalancesAfterClaim(relayerA); } @@ -509,7 +510,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { skip(CLAIM_DELAY + 1); expectBridgeDepositClaimed({bridgeTx: ethTx, txId: txId, relayer: relayerA, to: relayerA}); claim({caller: caller, bridgeTx: ethTx, to: address(0)}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); checkEthBalancesAfterClaim(relayerA); } @@ -521,7 +522,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { skip(CLAIM_DELAY + 1); expectBridgeDepositClaimed({bridgeTx: ethTx, txId: txId, relayer: relayerA, to: claimTo}); claim({caller: relayerA, bridgeTx: ethTx, to: claimTo}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); checkEthBalancesAfterClaim(claimTo); } @@ -533,7 +534,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { skip(CLAIM_DELAY + 30 days); expectBridgeDepositClaimed({bridgeTx: ethTx, txId: txId, relayer: relayerA, to: relayerA}); claim({caller: relayerA, bridgeTx: ethTx, to: relayerA}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); checkEthBalancesAfterClaim(relayerA); } @@ -613,7 +614,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); expectBridgeProofDisputed({txId: txId, guard: guard}); dispute({caller: guard, txId: txId}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); } @@ -625,7 +626,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { skip(CLAIM_DELAY); expectBridgeProofDisputed({txId: txId, guard: guard}); dispute({caller: guard, txId: txId}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); } @@ -637,7 +638,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); expectBridgeProofDisputed({txId: txId, guard: guard}); dispute({caller: guard, txId: txId}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethParams.originAmount); } @@ -650,7 +651,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { skip(CLAIM_DELAY); expectBridgeProofDisputed({txId: txId, guard: guard}); dispute({caller: guard, txId: txId}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethParams.originAmount); } @@ -713,7 +714,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { skip(DEADLINE + 1); expectBridgeDepositRefunded({bridgeParams: tokenParams, txId: txId}); refund({caller: refunder, bridgeTx: tokenTx}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + tokenParams.originAmount); assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN); @@ -726,7 +727,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { skip(DEADLINE + 1); expectBridgeDepositRefunded({bridgeParams: tokenParams, txId: txId}); refund({caller: refunder, bridgeTx: tokenTx}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + 2 * tokenParams.originAmount); assertEq(srcToken.balanceOf(userB), LEFTOVER_BALANCE); @@ -739,7 +740,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { skip(DEADLINE + 30 days); expectBridgeDepositRefunded({bridgeParams: tokenParams, txId: txId}); refund({caller: refunder, bridgeTx: tokenTx}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + tokenParams.originAmount); assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN); @@ -752,7 +753,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { skip(DEADLINE + PERMISSIONLESS_REFUND_DELAY + 1); expectBridgeDepositRefunded({bridgeParams: tokenParams, txId: txId}); refund({caller: caller, bridgeTx: tokenTx}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + tokenParams.originAmount); assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN); @@ -765,7 +766,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { skip(DEADLINE + 1); expectBridgeDepositRefunded({bridgeParams: ethParams, txId: txId}); refund({caller: refunder, bridgeTx: ethTx}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); assertEq(address(userA).balance, LEFTOVER_BALANCE + ethParams.originAmount); assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); @@ -779,7 +780,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { skip(DEADLINE + 1); expectBridgeDepositRefunded({bridgeParams: ethParams, txId: txId}); refund({caller: refunder, bridgeTx: ethTx}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); assertEq(address(userA).balance, LEFTOVER_BALANCE + 2 * ethParams.originAmount); assertEq(address(userB).balance, LEFTOVER_BALANCE); @@ -793,7 +794,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { skip(DEADLINE + 30 days); expectBridgeDepositRefunded({bridgeParams: ethParams, txId: txId}); refund({caller: refunder, bridgeTx: ethTx}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); assertEq(address(userA).balance, LEFTOVER_BALANCE + ethParams.originAmount); assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); @@ -807,7 +808,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { skip(DEADLINE + PERMISSIONLESS_REFUND_DELAY + 1); expectBridgeDepositRefunded({bridgeParams: ethParams, txId: txId}); refund({caller: caller, bridgeTx: ethTx}); - assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); assertEq(address(userA).balance, LEFTOVER_BALANCE + ethParams.originAmount); assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); diff --git a/packages/contracts-rfq/test/FastBridgeV2.t.sol b/packages/contracts-rfq/test/FastBridgeV2.t.sol index 8517a2dbf8..80c1354fa8 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.t.sol @@ -2,6 +2,8 @@ pragma solidity ^0.8.20; import {IFastBridge} from "../contracts/interfaces/IFastBridge.sol"; +import {IFastBridgeV2} from "../contracts/interfaces/IFastBridgeV2.sol"; + import {IFastBridgeV2Errors} from "../contracts/interfaces/IFastBridgeV2Errors.sol"; import {FastBridgeV2} from "../contracts/FastBridgeV2.sol"; From eae683077f8121a50fede14fe96c88a9c45a8423 Mon Sep 17 00:00:00 2001 From: parodime Date: Fri, 27 Sep 2024 23:51:55 +0000 Subject: [PATCH 59/90] Publish - @synapsecns/contracts-rfq@0.5.9 --- packages/contracts-rfq/CHANGELOG.md | 8 ++++++++ packages/contracts-rfq/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/contracts-rfq/CHANGELOG.md b/packages/contracts-rfq/CHANGELOG.md index 6306e5875e..e8b4d1fd9f 100644 --- a/packages/contracts-rfq/CHANGELOG.md +++ b/packages/contracts-rfq/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.9](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.8...@synapsecns/contracts-rfq@0.5.9) (2024-09-27) + +**Note:** Version bump only for package @synapsecns/contracts-rfq + + + + + ## [0.5.8](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.7...@synapsecns/contracts-rfq@0.5.8) (2024-09-27) diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index 54d6f8d6f1..99d1acfbee 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/contracts-rfq", "license": "MIT", - "version": "0.5.8", + "version": "0.5.9", "description": "FastBridge contracts.", "private": true, "files": [ From 5dbc3453badb3fb52da652762d6e9f441429b8b6 Mon Sep 17 00:00:00 2001 From: Moses <103143573+Defi-Moses@users.noreply.github.com> Date: Sun, 29 Sep 2024 12:11:03 +0100 Subject: [PATCH 60/90] adding originUserAddress for refunds [SLT-281] (#3206) * adding originUserAddress for refunds * Adds validation & tests * Reorder for required first --------- Co-authored-by: abtestingalpha --- .../src/controllers/bridgeController.ts | 14 ++++++- .../src/controllers/bridgeTxInfoController.ts | 16 ++++++-- packages/rest-api/src/routes/bridgeRoute.ts | 11 ++++++ .../rest-api/src/routes/bridgeTxInfoRoute.ts | 10 +++++ .../rest-api/src/tests/bridgeRoute.test.ts | 34 ++++++++++++++++ .../src/tests/bridgeTxInfoRoute.test.ts | 39 +++++++++++++++++++ 6 files changed, 119 insertions(+), 5 deletions(-) diff --git a/packages/rest-api/src/controllers/bridgeController.ts b/packages/rest-api/src/controllers/bridgeController.ts index 568c4eb0fc..f69c8715f0 100644 --- a/packages/rest-api/src/controllers/bridgeController.ts +++ b/packages/rest-api/src/controllers/bridgeController.ts @@ -11,7 +11,14 @@ export const bridgeController = async (req, res) => { return res.status(400).json({ errors: errors.array() }) } try { - const { fromChain, toChain, amount, fromToken, toToken } = req.query + const { + fromChain, + toChain, + amount, + fromToken, + toToken, + originUserAddress, + } = req.query const fromTokenInfo = tokenAddressToToken(fromChain.toString(), fromToken) const toTokenInfo = tokenAddressToToken(toChain.toString(), toToken) @@ -23,7 +30,10 @@ export const bridgeController = async (req, res) => { Number(toChain), fromToken, toToken, - amountInWei + amountInWei, + originUserAddress + ? { originUserAddress: originUserAddress.toString() } + : {} ) const payload = resp.map((quote) => { diff --git a/packages/rest-api/src/controllers/bridgeTxInfoController.ts b/packages/rest-api/src/controllers/bridgeTxInfoController.ts index 0a0b4bc7bc..01a56f01ca 100644 --- a/packages/rest-api/src/controllers/bridgeTxInfoController.ts +++ b/packages/rest-api/src/controllers/bridgeTxInfoController.ts @@ -11,8 +11,15 @@ export const bridgeTxInfoController = async (req, res) => { } try { - const { fromChain, toChain, amount, destAddress, fromToken, toToken } = - req.query + const { + fromChain, + toChain, + amount, + destAddress, + fromToken, + toToken, + originUserAddress, + } = req.query const fromTokenInfo = tokenAddressToToken(fromChain.toString(), fromToken) @@ -23,7 +30,10 @@ export const bridgeTxInfoController = async (req, res) => { Number(toChain), fromToken, toToken, - amountInWei + amountInWei, + originUserAddress + ? { originUserAddress: originUserAddress.toString() } + : {} ) const txInfoArray = await Promise.all( diff --git a/packages/rest-api/src/routes/bridgeRoute.ts b/packages/rest-api/src/routes/bridgeRoute.ts index 0e4a88b947..5c5d7b6c65 100644 --- a/packages/rest-api/src/routes/bridgeRoute.ts +++ b/packages/rest-api/src/routes/bridgeRoute.ts @@ -1,5 +1,6 @@ import express from 'express' import { check } from 'express-validator' +import { isAddress } from 'ethers/lib/utils' import { isTokenAddress } from '../utils/isTokenAddress' import { CHAINS_ARRAY } from '../constants/chains' @@ -49,6 +50,12 @@ const router = express.Router() * schema: * type: number * description: The amount of tokens to bridge + * - in: query + * name: originUserAddress + * required: false + * schema: + * type: string + * description: The address of the user on the origin chain * responses: * 200: * description: Successful response @@ -230,6 +237,10 @@ router.get( return validateRouteExists(fromChain, fromToken, toChain, toToken) }) .withMessage('No valid route exists for the chain/token combination'), + check('originUserAddress') + .optional() + .custom((value) => isAddress(value)) + .withMessage('Invalid originUserAddress address'), ], showFirstValidationError, bridgeController diff --git a/packages/rest-api/src/routes/bridgeTxInfoRoute.ts b/packages/rest-api/src/routes/bridgeTxInfoRoute.ts index 5a0ce30326..44b6904e08 100644 --- a/packages/rest-api/src/routes/bridgeTxInfoRoute.ts +++ b/packages/rest-api/src/routes/bridgeTxInfoRoute.ts @@ -56,6 +56,12 @@ const router = express.Router() * schema: * type: string * description: The destination address for the bridged tokens + * - in: query + * name: originUserAddress + * required: false + * schema: + * type: string + * description: The address of the user on the origin chain * responses: * 200: * description: Successful response @@ -171,6 +177,10 @@ router.get( return validateRouteExists(fromChain, fromToken, toChain, toToken) }) .withMessage('No valid route exists for the chain/token combination'), + check('originUserAddress') + .optional() + .custom((value) => isAddress(value)) + .withMessage('Invalid originUserAddress address'), ], showFirstValidationError, bridgeTxInfoController diff --git a/packages/rest-api/src/tests/bridgeRoute.test.ts b/packages/rest-api/src/tests/bridgeRoute.test.ts index 3617d5d71b..542feab63a 100644 --- a/packages/rest-api/src/tests/bridgeRoute.test.ts +++ b/packages/rest-api/src/tests/bridgeRoute.test.ts @@ -25,6 +25,23 @@ describe('Bridge Route with Real Synapse Service', () => { expect(response.body[0]).toHaveProperty('bridgeFeeFormatted') }, 15000) + it('should return bridge quotes for valid originUserAddress', async () => { + const response = await request(app).get('/bridge').query({ + fromChain: '1', + toChain: '10', + fromToken: USDC.addresses[1], + toToken: USDC.addresses[10], + amount: '1000', + originUserAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', + }) + + expect(response.status).toBe(200) + expect(Array.isArray(response.body)).toBe(true) + expect(response.body.length).toBeGreaterThan(0) + expect(response.body[0]).toHaveProperty('maxAmountOutStr') + expect(response.body[0]).toHaveProperty('bridgeFeeFormatted') + }, 15000) + it('should return bridge quotes for ZeroAddress', async () => { const response = await request(app).get('/bridge').query({ fromChain: '1', @@ -56,6 +73,23 @@ describe('Bridge Route with Real Synapse Service', () => { expect(response.body[0]).toHaveProperty('bridgeFeeFormatted') }, 15000) + it('should return 400 for invalid originUserAddress', async () => { + const response = await request(app).get('/bridge').query({ + fromChain: '1', + toChain: '10', + fromToken: USDC.addresses[1], + toToken: USDC.addresses[10], + amount: '1000', + originUserAddress: 'invalid_address', + }) + + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty( + 'message', + 'Invalid originUserAddress address' + ) + }, 15000) + it('should return 400 for unsupported route', async () => { const response = await request(app).get('/bridge').query({ fromChain: '1', diff --git a/packages/rest-api/src/tests/bridgeTxInfoRoute.test.ts b/packages/rest-api/src/tests/bridgeTxInfoRoute.test.ts index 9f5d183bbe..e39450e725 100644 --- a/packages/rest-api/src/tests/bridgeTxInfoRoute.test.ts +++ b/packages/rest-api/src/tests/bridgeTxInfoRoute.test.ts @@ -29,6 +29,45 @@ describe('Bridge TX Info Route', () => { ) }, 10_000) + it('should return bridge transaction info for valid input with valid originUserAddress', async () => { + const response = await request(app).get('/bridgeTxInfo').query({ + fromChain: '1', + toChain: '137', + fromToken: USDC.addresses[1], + toToken: USDC.addresses[137], + amount: '1000', + destAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', + originUserAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', + }) + + expect(response.status).toBe(200) + expect(Array.isArray(response.body)).toBe(true) + expect(response.body.length).toBeGreaterThan(0) + expect(response.body[0]).toHaveProperty('data') + expect(response.body[0]).toHaveProperty( + 'to', + '0xd5a597d6e7ddf373a92C8f477DAAA673b0902F48' + ) + }, 10_000) + + it('should return 400 for invalid originUserAddress', async () => { + const response = await request(app).get('/bridgeTxInfo').query({ + fromChain: '1', + toChain: '137', + fromToken: USDC.addresses[1], + toToken: USDC.addresses[137], + amount: '1000', + destAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', + originUserAddress: 'invalid_address', + }) + + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty( + 'message', + 'Invalid originUserAddress address' + ) + }, 10_000) + it('should return 400 for unsupported route', async () => { const response = await request(app).get('/bridgeTxInfo').query({ fromChain: '1', From 44338bea75a5d135aea59d6ec2ce5a8d3593c6ee Mon Sep 17 00:00:00 2001 From: Defi-Moses Date: Sun, 29 Sep 2024 11:15:06 +0000 Subject: [PATCH 61/90] Publish - @synapsecns/rest-api@1.3.4 --- packages/rest-api/CHANGELOG.md | 8 ++++++++ packages/rest-api/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/rest-api/CHANGELOG.md b/packages/rest-api/CHANGELOG.md index d7ee944825..1c017e63fa 100644 --- a/packages/rest-api/CHANGELOG.md +++ b/packages/rest-api/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.3.4](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.3.3...@synapsecns/rest-api@1.3.4) (2024-09-29) + +**Note:** Version bump only for package @synapsecns/rest-api + + + + + ## [1.3.3](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.3.2...@synapsecns/rest-api@1.3.3) (2024-09-26) **Note:** Version bump only for package @synapsecns/rest-api diff --git a/packages/rest-api/package.json b/packages/rest-api/package.json index 14e8e92496..fa41f97411 100644 --- a/packages/rest-api/package.json +++ b/packages/rest-api/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/rest-api", - "version": "1.3.3", + "version": "1.3.4", "private": "true", "engines": { "node": ">=18.17.0" From 28bb278a0f3cfd4df81893487150ebbc84488d14 Mon Sep 17 00:00:00 2001 From: abtestingalpha <104046418+abtestingalpha@users.noreply.github.com> Date: Mon, 30 Sep 2024 18:05:50 -0400 Subject: [PATCH 62/90] Add height/width fields for future constants upgrade integration (#3208) --- .../explorer-ui/components/misc/AssetImage.tsx | 18 ++++++++++++++++-- .../explorer-ui/components/misc/ChainImage.tsx | 2 ++ .../explorer-ui/components/misc/ChainInfo.tsx | 2 ++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/explorer-ui/components/misc/AssetImage.tsx b/packages/explorer-ui/components/misc/AssetImage.tsx index 570d70429e..5206152f11 100644 --- a/packages/explorer-ui/components/misc/AssetImage.tsx +++ b/packages/explorer-ui/components/misc/AssetImage.tsx @@ -11,7 +11,13 @@ export const AssetImage = ({ tokenAddress, chainId, className }) => {
- +
@@ -29,13 +35,21 @@ export const AssetImage = ({ tokenAddress, chainId, className }) => { className={`inline mr-[.5rem] rounded-full ${className}`} src={t?.icon} alt="" + height={16} + width={16} />
) } else { return ( // temporary fix until either symbolToToken works better as a function or explorer indexer has the right token addresses - + // { src={chain.chainImg} className={`${imgSize} rounded-full mr-2 inline ${className}`} alt={chain.name} + height={16} + width={16} /> ) } else { diff --git a/packages/explorer-ui/components/misc/ChainInfo.tsx b/packages/explorer-ui/components/misc/ChainInfo.tsx index d494281bba..06a40e0980 100644 --- a/packages/explorer-ui/components/misc/ChainInfo.tsx +++ b/packages/explorer-ui/components/misc/ChainInfo.tsx @@ -45,6 +45,8 @@ export const ChainInfo = ({ className={`inline rounded-full ${imgClassName}`} src={chain?.chainImg} alt={chain?.name} + height={16} + width={16} />

Date: Mon, 30 Sep 2024 22:09:52 +0000 Subject: [PATCH 63/90] Publish - @synapsecns/explorer-ui@0.3.4 --- packages/explorer-ui/CHANGELOG.md | 8 ++++++++ packages/explorer-ui/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/explorer-ui/CHANGELOG.md b/packages/explorer-ui/CHANGELOG.md index 3367d054fe..10e44ec05a 100644 --- a/packages/explorer-ui/CHANGELOG.md +++ b/packages/explorer-ui/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.4](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.3...@synapsecns/explorer-ui@0.3.4) (2024-09-30) + +**Note:** Version bump only for package @synapsecns/explorer-ui + + + + + ## [0.3.3](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.2...@synapsecns/explorer-ui@0.3.3) (2024-09-26) **Note:** Version bump only for package @synapsecns/explorer-ui diff --git a/packages/explorer-ui/package.json b/packages/explorer-ui/package.json index 980a2fd805..ba8c2ff199 100644 --- a/packages/explorer-ui/package.json +++ b/packages/explorer-ui/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/explorer-ui", - "version": "0.3.3", + "version": "0.3.4", "private": true, "engines": { "node": ">=18.17.0" From eead134ff4d19554413efdab732c38e9d90fe763 Mon Sep 17 00:00:00 2001 From: parodime Date: Tue, 1 Oct 2024 05:49:33 -0400 Subject: [PATCH 64/90] solhint warning fixes [SLT-287] (#3207) * solhint warning fixes [SLT-287] * named vars on overloads --- .../contracts-rfq/contracts/FastBridgeV2.sol | 212 +++++++++--------- .../contracts-rfq/test/FastBridgeV2.t.sol | 2 + 2 files changed, 108 insertions(+), 106 deletions(-) diff --git a/packages/contracts-rfq/contracts/FastBridgeV2.sol b/packages/contracts-rfq/contracts/FastBridgeV2.sol index e649ab0ec0..b6d49ac9dd 100644 --- a/packages/contracts-rfq/contracts/FastBridgeV2.sol +++ b/packages/contracts-rfq/contracts/FastBridgeV2.sol @@ -31,49 +31,14 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { /// @dev to prevent replays uint256 public nonce; + // @dev the block the contract was deployed at uint256 public immutable deployBlock; - function bridgeStatuses(bytes32 transactionId) public view returns (BridgeStatus status) { - return bridgeTxDetails[transactionId].status; - } - - function bridgeProofs(bytes32 transactionId) public view returns (uint96 timestamp, address relayer) { - timestamp = bridgeTxDetails[transactionId].proofBlockTimestamp; - relayer = bridgeTxDetails[transactionId].proofRelayer; - } - constructor(address _owner) Admin(_owner) { deployBlock = block.number; } - /// @notice Pulls a requested token from the user to the requested recipient. - /// @dev Be careful of re-entrancy issues when msg.value > 0 and recipient != address(this) - function _pullToken(address recipient, address token, uint256 amount) internal returns (uint256 amountPulled) { - if (token != UniversalTokenLib.ETH_ADDRESS) { - token.assertIsContract(); - // Record token balance before transfer - amountPulled = IERC20(token).balanceOf(recipient); - // Token needs to be pulled only if msg.value is zero - // This way user can specify WETH as the origin asset - IERC20(token).safeTransferFrom(msg.sender, recipient, amount); - // Use the difference between the recorded balance and the current balance as the amountPulled - amountPulled = IERC20(token).balanceOf(recipient) - amountPulled; - } else { - // Otherwise, we need to check that ETH amount matches msg.value - if (amount != msg.value) revert MsgValueIncorrect(); - // Transfer value to recipient if not this address - if (recipient != address(this)) token.universalTransfer(recipient, amount); - // We will forward msg.value in the external call later, if recipient is not this contract - amountPulled = msg.value; - } - } - - /// @inheritdoc IFastBridge - function getBridgeTransaction(bytes memory request) public pure returns (BridgeTransaction memory) { - return abi.decode(request, (BridgeTransaction)); - } - /// @inheritdoc IFastBridge function bridge(BridgeParams memory params) external payable { // check bridge params @@ -127,7 +92,68 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { /// @inheritdoc IFastBridge function relay(bytes memory request) external payable { - relay(request, msg.sender); + relay({request: request, relayer: msg.sender}); + } + + /// @inheritdoc IFastBridge + function prove(bytes memory request, bytes32 destTxHash) external { + prove({transactionId: keccak256(request), destTxHash: destTxHash, relayer: msg.sender}); + } + + /// @inheritdoc IFastBridgeV2 + function claim(bytes memory request) external { + claim({request: request, to: address(0)}); + } + + /// @inheritdoc IFastBridge + function dispute(bytes32 transactionId) external onlyRole(GUARD_ROLE) { + if (bridgeTxDetails[transactionId].status != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect(); + if (_timeSince(bridgeTxDetails[transactionId].proofBlockTimestamp) > DISPUTE_PERIOD) { + revert DisputePeriodPassed(); + } + + // @dev relayer gets slashed effectively if dest relay has gone thru + bridgeTxDetails[transactionId].status = BridgeStatus.REQUESTED; + bridgeTxDetails[transactionId].proofRelayer = address(0); + bridgeTxDetails[transactionId].proofBlockTimestamp = 0; + bridgeTxDetails[transactionId].proofBlockNumber = 0; + + emit BridgeProofDisputed(transactionId, msg.sender); + } + + /// @inheritdoc IFastBridge + function refund(bytes memory request) external { + bytes32 transactionId = keccak256(request); + + BridgeTransaction memory transaction = getBridgeTransaction(request); + + if (bridgeTxDetails[transactionId].status != BridgeStatus.REQUESTED) revert StatusIncorrect(); + + if (hasRole(REFUNDER_ROLE, msg.sender)) { + // Refunder can refund if deadline has passed + if (block.timestamp <= transaction.deadline) revert DeadlineNotExceeded(); + } else { + // Permissionless refund is allowed after REFUND_DELAY + if (block.timestamp <= transaction.deadline + REFUND_DELAY) revert DeadlineNotExceeded(); + } + + // if all checks passed, set to REFUNDED status + bridgeTxDetails[transactionId].status = BridgeStatus.REFUNDED; + + // transfer origin collateral back to original sender + address to = transaction.originSender; + address token = transaction.originToken; + uint256 amount = transaction.originAmount + transaction.originFeeAmount; + token.universalTransfer(to, amount); + + emit BridgeDepositRefunded(transactionId, to, token, amount); + } + + /// @inheritdoc IFastBridge + function canClaim(bytes32 transactionId, address relayer) external view returns (bool) { + if (bridgeTxDetails[transactionId].status != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect(); + if (bridgeTxDetails[transactionId].proofRelayer != relayer) revert SenderIncorrect(); + return _timeSince(bridgeTxDetails[transactionId].proofBlockTimestamp) > DISPUTE_PERIOD; } /// @inheritdoc IFastBridgeV2 @@ -178,18 +204,6 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { ); } - /// @inheritdoc IFastBridgeV2 - function bridgeRelays(bytes32 transactionId) public view returns (bool) { - // has this transactionId been relayed? - return bridgeRelayDetails[transactionId].relayer != address(0); - } - - /// @inheritdoc IFastBridge - function prove(bytes memory request, bytes32 destTxHash) external { - bytes32 transactionId = keccak256(request); - prove(transactionId, destTxHash, msg.sender); - } - /// @inheritdoc IFastBridgeV2 function prove(bytes32 transactionId, bytes32 destTxHash, address relayer) public onlyRole(RELAYER_ROLE) { // update bridge tx status given proof provided @@ -202,30 +216,6 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { emit BridgeProofProvided(transactionId, relayer, destTxHash); } - /// @notice Calculates time since proof submitted - /// @dev proof.timestamp stores casted uint40(block.timestamp) block timestamps for gas optimization - /// _timeSince(proof) can accomodate rollover case when block.timestamp > type(uint40).max but - /// proof.timestamp < type(uint40).max via unchecked statement - /// @param proofBlockTimestamp The bridge proof block timestamp - /// @return delta Time delta since proof submitted - function _timeSince(uint40 proofBlockTimestamp) internal view returns (uint256 delta) { - unchecked { - delta = uint40(block.timestamp) - proofBlockTimestamp; - } - } - - /// @inheritdoc IFastBridge - function canClaim(bytes32 transactionId, address relayer) external view returns (bool) { - if (bridgeTxDetails[transactionId].status != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect(); - if (bridgeTxDetails[transactionId].proofRelayer != relayer) revert SenderIncorrect(); - return _timeSince(bridgeTxDetails[transactionId].proofBlockTimestamp) > DISPUTE_PERIOD; - } - - /// @inheritdoc IFastBridgeV2 - function claim(bytes memory request) external { - claim(request, address(0)); - } - /// @inheritdoc IFastBridge function claim(bytes memory request, address to) public { bytes32 transactionId = keccak256(request); @@ -258,47 +248,57 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { emit BridgeDepositClaimed(transactionId, bridgeTxDetails[transactionId].proofRelayer, to, token, amount); } - /// @inheritdoc IFastBridge - function dispute(bytes32 transactionId) external onlyRole(GUARD_ROLE) { - if (bridgeTxDetails[transactionId].status != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect(); - if (_timeSince(bridgeTxDetails[transactionId].proofBlockTimestamp) > DISPUTE_PERIOD) { - revert DisputePeriodPassed(); - } + function bridgeStatuses(bytes32 transactionId) public view returns (BridgeStatus status) { + return bridgeTxDetails[transactionId].status; + } - // @dev relayer gets slashed effectively if dest relay has gone thru - bridgeTxDetails[transactionId].status = BridgeStatus.REQUESTED; - bridgeTxDetails[transactionId].proofRelayer = address(0); - bridgeTxDetails[transactionId].proofBlockTimestamp = 0; - bridgeTxDetails[transactionId].proofBlockNumber = 0; + function bridgeProofs(bytes32 transactionId) public view returns (uint96 timestamp, address relayer) { + timestamp = bridgeTxDetails[transactionId].proofBlockTimestamp; + relayer = bridgeTxDetails[transactionId].proofRelayer; + } - emit BridgeProofDisputed(transactionId, msg.sender); + /// @inheritdoc IFastBridgeV2 + function bridgeRelays(bytes32 transactionId) public view returns (bool) { + // has this transactionId been relayed? + return bridgeRelayDetails[transactionId].relayer != address(0); } /// @inheritdoc IFastBridge - function refund(bytes memory request) external { - bytes32 transactionId = keccak256(request); - - BridgeTransaction memory transaction = getBridgeTransaction(request); - - if (bridgeTxDetails[transactionId].status != BridgeStatus.REQUESTED) revert StatusIncorrect(); + function getBridgeTransaction(bytes memory request) public pure returns (BridgeTransaction memory) { + return abi.decode(request, (BridgeTransaction)); + } - if (hasRole(REFUNDER_ROLE, msg.sender)) { - // Refunder can refund if deadline has passed - if (block.timestamp <= transaction.deadline) revert DeadlineNotExceeded(); + /// @notice Pulls a requested token from the user to the requested recipient. + /// @dev Be careful of re-entrancy issues when msg.value > 0 and recipient != address(this) + function _pullToken(address recipient, address token, uint256 amount) internal returns (uint256 amountPulled) { + if (token != UniversalTokenLib.ETH_ADDRESS) { + token.assertIsContract(); + // Record token balance before transfer + amountPulled = IERC20(token).balanceOf(recipient); + // Token needs to be pulled only if msg.value is zero + // This way user can specify WETH as the origin asset + IERC20(token).safeTransferFrom(msg.sender, recipient, amount); + // Use the difference between the recorded balance and the current balance as the amountPulled + amountPulled = IERC20(token).balanceOf(recipient) - amountPulled; } else { - // Permissionless refund is allowed after REFUND_DELAY - if (block.timestamp <= transaction.deadline + REFUND_DELAY) revert DeadlineNotExceeded(); + // Otherwise, we need to check that ETH amount matches msg.value + if (amount != msg.value) revert MsgValueIncorrect(); + // Transfer value to recipient if not this address + if (recipient != address(this)) token.universalTransfer(recipient, amount); + // We will forward msg.value in the external call later, if recipient is not this contract + amountPulled = msg.value; } + } - // if all checks passed, set to REFUNDED status - bridgeTxDetails[transactionId].status = BridgeStatus.REFUNDED; - - // transfer origin collateral back to original sender - address to = transaction.originSender; - address token = transaction.originToken; - uint256 amount = transaction.originAmount + transaction.originFeeAmount; - token.universalTransfer(to, amount); - - emit BridgeDepositRefunded(transactionId, to, token, amount); + /// @notice Calculates time since proof submitted + /// @dev proof.timestamp stores casted uint40(block.timestamp) block timestamps for gas optimization + /// _timeSince(proof) can accomodate rollover case when block.timestamp > type(uint40).max but + /// proof.timestamp < type(uint40).max via unchecked statement + /// @param proofBlockTimestamp The bridge proof block timestamp + /// @return delta Time delta since proof submitted + function _timeSince(uint40 proofBlockTimestamp) internal view returns (uint256 delta) { + unchecked { + delta = uint40(block.timestamp) - proofBlockTimestamp; + } } } diff --git a/packages/contracts-rfq/test/FastBridgeV2.t.sol b/packages/contracts-rfq/test/FastBridgeV2.t.sol index 80c1354fa8..ab87618149 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.t.sol @@ -2,6 +2,8 @@ pragma solidity ^0.8.20; import {IFastBridge} from "../contracts/interfaces/IFastBridge.sol"; + +// solhint-disable-next-line no-unused-import import {IFastBridgeV2} from "../contracts/interfaces/IFastBridgeV2.sol"; import {IFastBridgeV2Errors} from "../contracts/interfaces/IFastBridgeV2Errors.sol"; From d86c4f46b7fcd5364b8c9eee57dafcfc9c5ca822 Mon Sep 17 00:00:00 2001 From: parodime Date: Tue, 1 Oct 2024 09:53:43 +0000 Subject: [PATCH 65/90] Publish - @synapsecns/contracts-rfq@0.5.10 --- packages/contracts-rfq/CHANGELOG.md | 8 ++++++++ packages/contracts-rfq/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/contracts-rfq/CHANGELOG.md b/packages/contracts-rfq/CHANGELOG.md index e8b4d1fd9f..5b7b105e5a 100644 --- a/packages/contracts-rfq/CHANGELOG.md +++ b/packages/contracts-rfq/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.10](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.9...@synapsecns/contracts-rfq@0.5.10) (2024-10-01) + +**Note:** Version bump only for package @synapsecns/contracts-rfq + + + + + ## [0.5.9](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.8...@synapsecns/contracts-rfq@0.5.9) (2024-09-27) **Note:** Version bump only for package @synapsecns/contracts-rfq diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index 99d1acfbee..6e12c82fd7 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/contracts-rfq", "license": "MIT", - "version": "0.5.9", + "version": "0.5.10", "description": "FastBridge contracts.", "private": true, "files": [ From dfd670182a98a79af89c4b851a72cac4d471b3c5 Mon Sep 17 00:00:00 2001 From: aureliusbtc <82057759+aureliusbtc@users.noreply.github.com> Date: Tue, 1 Oct 2024 11:38:10 -0400 Subject: [PATCH 66/90] update bl --- packages/synapse-interface/public/blacklist.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/synapse-interface/public/blacklist.json b/packages/synapse-interface/public/blacklist.json index 123e342197..787455be23 100644 --- a/packages/synapse-interface/public/blacklist.json +++ b/packages/synapse-interface/public/blacklist.json @@ -539,5 +539,7 @@ "0x610Df4B3D1f8528073d8710b6BdBbb86121ac6f3", "0x93c95714fbd1d9184f22aa82785478ff4e71a8e2", "0x800f06b394da4c98e88a0da09f0fb1eb280f9636", - "0xf7e8033366166f92eb477b7b38e0d47d47b43326" + "0xf7e8033366166f92eb477b7b38e0d47d47b43326", + "0xc01f4307DAa6A2d9e01303B194E77fBEF29Fe904", + "0xFAF26EdB4E4b43B7A513808d59913FC43A705D74" ] From eb3db7ceb781feaa84f606869d9f9ffee5928cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CF=87=C2=B2?= <88190723+ChiTimesChi@users.noreply.github.com> Date: Tue, 1 Oct 2024 17:03:09 +0100 Subject: [PATCH 67/90] feat(contracts-rfq): relayer exclusivity [SLT-187] (#3202) * feat: scaffold exclusivity params * test: update to use V2 structs * test: exclusivity on DST chain * test: exclusivity on SRC chain * feat: exclusivity params in bridge * feat: update decoding in relay * feat: check for exclusivity period in relay * test: disable parity tests (no longer backwards compatible) * test: expect quoteID-related event * feat: additional event for tracking quoteID * fix: ignore code-complexity error for now * test: more coverage for `bridgeRelayDetails` * refactor: remove unnecessary casts in tests * refactor: move txId check, comments * test: update for changes from #3204 * test: benchmark for SRC exclusivity * test: benchmark for DST exclusivity * fix: decode into BridgeTransactionV2 Note: technically this isn't necessary AS OF NOW, as V2 struct new fields are ignored by the V1 decoding func. * test: coverage for V1, V2 encoding * test: coverage for using V1 request instead of V2 * refactor: unroll the nested v2 structure * test: update for the unrolled struct * refactor: make backwards-compatible view external * chore: `foundryup` -> `forge fmt` yes, this is slightly annoying :( * refactor: rename event * fix: post-merge getBridgeTransaction -> getBridgeTransactionV2 * refactor: move public `bridge()`, named vars * test: use `quoteRelayer` as exclusivity flag * fix: always use `quoteExclusivitySeconds` as offset in `bridge()` * fix: don't check `exclusivityEndTime` on relays when `exclusivityRelayer` is not set * test: add cases for negative `quoteExclusivitySeconds` * test: enforce `0 < exclusivityEndTime <= deadline` * feat: negative `quoteExclusivitySeconds` --- .../contracts-rfq/contracts/FastBridgeV2.sol | 151 +++++++++------- .../contracts/interfaces/IFastBridgeV2.sol | 46 +++++ .../interfaces/IFastBridgeV2Errors.sol | 2 + .../test/FastBridgeV2.Dst.Base.t.sol | 8 +- .../test/FastBridgeV2.Dst.Exclusivity.t.sol | 163 ++++++++++++++++++ .../contracts-rfq/test/FastBridgeV2.Dst.t.sol | 90 ++++++---- .../test/FastBridgeV2.Encoding.t.sol | 67 +++++++ .../test/FastBridgeV2.GasBench.Dst.Excl.t.sol | 25 +++ .../test/FastBridgeV2.GasBench.Src.t.sol | 47 ++++- .../test/FastBridgeV2.Parity.t.sol | 4 +- .../test/FastBridgeV2.Src.Base.t.sol | 24 ++- ...astBridgeV2.Src.Exclusivity.Negative.t.sol | 52 ++++++ .../test/FastBridgeV2.Src.Exclusivity.t.sol | 40 +++++ .../contracts-rfq/test/FastBridgeV2.Src.t.sol | 39 +++-- .../contracts-rfq/test/FastBridgeV2.t.sol | 133 ++++++++++---- 15 files changed, 736 insertions(+), 155 deletions(-) create mode 100644 packages/contracts-rfq/test/FastBridgeV2.Dst.Exclusivity.t.sol create mode 100644 packages/contracts-rfq/test/FastBridgeV2.Encoding.t.sol create mode 100644 packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.Excl.t.sol create mode 100644 packages/contracts-rfq/test/FastBridgeV2.Src.Exclusivity.Negative.t.sol create mode 100644 packages/contracts-rfq/test/FastBridgeV2.Src.Exclusivity.t.sol diff --git a/packages/contracts-rfq/contracts/FastBridgeV2.sol b/packages/contracts-rfq/contracts/FastBridgeV2.sol index b6d49ac9dd..67221da8d8 100644 --- a/packages/contracts-rfq/contracts/FastBridgeV2.sol +++ b/packages/contracts-rfq/contracts/FastBridgeV2.sol @@ -41,53 +41,10 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { /// @inheritdoc IFastBridge function bridge(BridgeParams memory params) external payable { - // check bridge params - if (params.dstChainId == block.chainid) revert ChainIncorrect(); - if (params.originAmount == 0 || params.destAmount == 0) revert AmountIncorrect(); - if (params.sender == address(0) || params.to == address(0)) revert ZeroAddress(); - if (params.originToken == address(0) || params.destToken == address(0)) revert ZeroAddress(); - if (params.deadline < block.timestamp + MIN_DEADLINE_PERIOD) revert DeadlineTooShort(); - - // transfer tokens to bridge contract - // @dev use returned originAmount in request in case of transfer fees - uint256 originAmount = _pullToken(address(this), params.originToken, params.originAmount); - - // track amount of origin token owed to protocol - uint256 originFeeAmount; - if (protocolFeeRate > 0) originFeeAmount = (originAmount * protocolFeeRate) / FEE_BPS; - originAmount -= originFeeAmount; // remove from amount used in request as not relevant for relayers - - // set status to requested - bytes memory request = abi.encode( - BridgeTransaction({ - originChainId: uint32(block.chainid), - destChainId: params.dstChainId, - originSender: params.sender, - destRecipient: params.to, - originToken: params.originToken, - destToken: params.destToken, - originAmount: originAmount, - destAmount: params.destAmount, - originFeeAmount: originFeeAmount, - sendChainGas: params.sendChainGas, - deadline: params.deadline, - nonce: nonce++ // increment nonce on every bridge - }) - ); - bytes32 transactionId = keccak256(request); - bridgeTxDetails[transactionId].status = BridgeStatus.REQUESTED; - - emit BridgeRequested( - transactionId, - params.sender, - request, - params.dstChainId, - params.originToken, - params.destToken, - originAmount, - params.destAmount, - params.sendChainGas - ); + bridge({ + params: params, + paramsV2: BridgeParamsV2({quoteRelayer: address(0), quoteExclusivitySeconds: 0, quoteId: bytes("")}) + }); } /// @inheritdoc IFastBridge @@ -125,7 +82,7 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { function refund(bytes memory request) external { bytes32 transactionId = keccak256(request); - BridgeTransaction memory transaction = getBridgeTransaction(request); + BridgeTransactionV2 memory transaction = getBridgeTransactionV2(request); if (bridgeTxDetails[transactionId].status != BridgeStatus.REQUESTED) revert StatusIncorrect(); @@ -156,18 +113,96 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { return _timeSince(bridgeTxDetails[transactionId].proofBlockTimestamp) > DISPUTE_PERIOD; } + /// @inheritdoc IFastBridge + function getBridgeTransaction(bytes memory request) external pure returns (BridgeTransaction memory) { + // Note: when passing V2 request, this will decode the V1 fields correctly since the new fields were + // added as the last fields of the struct and hence the ABI decoder will simply ignore the extra data. + return abi.decode(request, (BridgeTransaction)); + } + + /// @inheritdoc IFastBridgeV2 + // TODO: reduce cyclomatic complexity alongside arbitrary call + // solhint-disable-next-line code-complexity + function bridge(BridgeParams memory params, BridgeParamsV2 memory paramsV2) public payable { + // check bridge params + if (params.dstChainId == block.chainid) revert ChainIncorrect(); + if (params.originAmount == 0 || params.destAmount == 0) revert AmountIncorrect(); + if (params.sender == address(0) || params.to == address(0)) revert ZeroAddress(); + if (params.originToken == address(0) || params.destToken == address(0)) revert ZeroAddress(); + if (params.deadline < block.timestamp + MIN_DEADLINE_PERIOD) revert DeadlineTooShort(); + int256 exclusivityEndTime = int256(block.timestamp) + paramsV2.quoteExclusivitySeconds; + // exclusivityEndTime must be in range (0 .. params.deadline] + if (exclusivityEndTime <= 0 || exclusivityEndTime > int256(params.deadline)) { + revert ExclusivityParamsIncorrect(); + } + // transfer tokens to bridge contract + // @dev use returned originAmount in request in case of transfer fees + uint256 originAmount = _pullToken(address(this), params.originToken, params.originAmount); + + // track amount of origin token owed to protocol + uint256 originFeeAmount; + if (protocolFeeRate > 0) originFeeAmount = (originAmount * protocolFeeRate) / FEE_BPS; + originAmount -= originFeeAmount; // remove from amount used in request as not relevant for relayers + + // set status to requested + bytes memory request = abi.encode( + BridgeTransactionV2({ + originChainId: uint32(block.chainid), + destChainId: params.dstChainId, + originSender: params.sender, + destRecipient: params.to, + originToken: params.originToken, + destToken: params.destToken, + originAmount: originAmount, + destAmount: params.destAmount, + originFeeAmount: originFeeAmount, + sendChainGas: params.sendChainGas, + deadline: params.deadline, + nonce: nonce++, // increment nonce on every bridge + exclusivityRelayer: paramsV2.quoteRelayer, + // We checked exclusivityEndTime to be in range (0 .. params.deadline] above, so can safely cast + exclusivityEndTime: uint256(exclusivityEndTime) + }) + ); + bytes32 transactionId = keccak256(request); + bridgeTxDetails[transactionId].status = BridgeStatus.REQUESTED; + + emit BridgeRequested( + transactionId, + params.sender, + request, + params.dstChainId, + params.originToken, + params.destToken, + originAmount, + params.destAmount, + params.sendChainGas + ); + emit BridgeQuoteDetails(transactionId, paramsV2.quoteId); + } + /// @inheritdoc IFastBridgeV2 + // TODO: reduce cyclomatic complexity alongside arbitrary call + // solhint-disable-next-line code-complexity function relay(bytes memory request, address relayer) public payable { if (relayer == address(0)) revert ZeroAddress(); + // Check if the transaction has already been relayed bytes32 transactionId = keccak256(request); - BridgeTransaction memory transaction = getBridgeTransaction(request); + if (bridgeRelays(transactionId)) revert TransactionRelayed(); + // Decode the transaction and check that it could be relayed on this chain + BridgeTransactionV2 memory transaction = getBridgeTransactionV2(request); if (transaction.destChainId != uint32(block.chainid)) revert ChainIncorrect(); - - // check haven't exceeded deadline for relay to happen + // Check the deadline for relay to happen if (block.timestamp > transaction.deadline) revert DeadlineExceeded(); - - if (bridgeRelayDetails[transactionId].relayer != address(0)) revert TransactionRelayed(); - + // Check the exclusivity period, if it is still ongoing + // forgefmt: disable-next-item + if ( + transaction.exclusivityRelayer != address(0) && + transaction.exclusivityRelayer != relayer && + block.timestamp <= transaction.exclusivityEndTime + ) { + revert ExclusivityPeriodNotPassed(); + } // mark bridge transaction as relayed bridgeRelayDetails[transactionId] = BridgeRelay({blockNumber: uint48(block.number), blockTimestamp: uint48(block.timestamp), relayer: relayer}); @@ -219,7 +254,7 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { /// @inheritdoc IFastBridge function claim(bytes memory request, address to) public { bytes32 transactionId = keccak256(request); - BridgeTransaction memory transaction = getBridgeTransaction(request); + BridgeTransactionV2 memory transaction = getBridgeTransactionV2(request); // update bridge tx status if able to claim origin collateral if (bridgeTxDetails[transactionId].status != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect(); @@ -263,9 +298,9 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { return bridgeRelayDetails[transactionId].relayer != address(0); } - /// @inheritdoc IFastBridge - function getBridgeTransaction(bytes memory request) public pure returns (BridgeTransaction memory) { - return abi.decode(request, (BridgeTransaction)); + /// @inheritdoc IFastBridgeV2 + function getBridgeTransactionV2(bytes memory request) public pure returns (BridgeTransactionV2 memory) { + return abi.decode(request, (BridgeTransactionV2)); } /// @notice Pulls a requested token from the user to the requested recipient. diff --git a/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol index 35c6173224..81a67636b5 100644 --- a/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol +++ b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol @@ -25,6 +25,48 @@ interface IFastBridgeV2 is IFastBridge { address relayer; } + /// @notice New params introduced in the FastBridgeV2. + /// We are passing fields from the older BridgeParams struct outside of this struct + /// for backwards compatibility. + /// Note: quoteRelayer and quoteExclusivitySeconds are either both zero (indicating no exclusivity) + /// or both non-zero (indicating exclusivity for the given period). + /// @param quoteRelayer Relayer that provided the quote for the transaction + /// @param quoteExclusivitySeconds Period of time the quote relayer is guaranteed exclusivity after user's deposit + /// @param quoteId Unique quote identifier used for tracking the quote + struct BridgeParamsV2 { + address quoteRelayer; + int256 quoteExclusivitySeconds; + bytes quoteId; + } + + /// @notice Updated bridge transaction struct to include parameters introduced in FastBridgeV2. + /// Note: only `exclusivityRelayer` can fill such a transaction until `exclusivityEndTime`. + /// TODO: consider changing the encoding scheme to prevent spending extra gas on decoding. + struct BridgeTransactionV2 { + uint32 originChainId; + uint32 destChainId; + address originSender; // user (origin) + address destRecipient; // user (dest) + address originToken; + address destToken; + uint256 originAmount; // amount in on origin bridge less originFeeAmount + uint256 destAmount; + uint256 originFeeAmount; + bool sendChainGas; + uint256 deadline; // user specified deadline for destination relay + uint256 nonce; + address exclusivityRelayer; + uint256 exclusivityEndTime; + } + + event BridgeQuoteDetails(bytes32 indexed transactionId, bytes quoteId); + + /// @notice Initiates bridge on origin chain to be relayed by off-chain relayer, with the ability + /// to provide temporary exclusivity fill rights for the quote relayer. + /// @param params The parameters required to bridge + /// @param paramsV2 The parameters for exclusivity fill rights (optional, could be left empty) + function bridge(BridgeParams memory params, BridgeParamsV2 memory paramsV2) external payable; + /// @notice Relays destination side of bridge transaction by off-chain relayer /// @param request The encoded bridge transaction to relay on destination chain /// @param relayer The address of the relaying entity which should have control of the origin funds when claimed @@ -55,4 +97,8 @@ interface IFastBridgeV2 is IFastBridge { /// @return timestamp The timestamp of the bridge proof /// @return relayer The relayer address of the bridge proof function bridgeProofs(bytes32 transactionId) external view returns (uint96 timestamp, address relayer); + + /// @notice Decodes bridge request into a bridge transaction V2 struct used by FastBridgeV2 + /// @param request The bridge request to decode + function getBridgeTransactionV2(bytes memory request) external view returns (BridgeTransactionV2 memory); } diff --git a/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2Errors.sol b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2Errors.sol index 70fd3d0e39..85f3bacdcf 100644 --- a/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2Errors.sol +++ b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2Errors.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.0; interface IFastBridgeV2Errors { error AmountIncorrect(); error ChainIncorrect(); + error ExclusivityParamsIncorrect(); error MsgValueIncorrect(); error SenderIncorrect(); error StatusIncorrect(); @@ -14,6 +15,7 @@ interface IFastBridgeV2Errors { error DeadlineTooShort(); error DisputePeriodNotPassed(); error DisputePeriodPassed(); + error ExclusivityPeriodNotPassed(); error TransactionRelayed(); } diff --git a/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol index bf113a2078..9e79e48489 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {FastBridgeV2, FastBridgeV2Test, IFastBridge} from "./FastBridgeV2.t.sol"; +import {FastBridgeV2, FastBridgeV2Test, IFastBridgeV2} from "./FastBridgeV2.t.sol"; // solhint-disable func-name-mixedcase, ordering contract FastBridgeV2DstBaseTest is FastBridgeV2Test { uint256 public constant LEFTOVER_BALANCE = 1 ether; - function setUp() public override { + function setUp() public virtual override { vm.chainId(DST_CHAIN_ID); super.setUp(); } @@ -29,7 +29,7 @@ contract FastBridgeV2DstBaseTest is FastBridgeV2Test { // ══════════════════════════════════════════════════ HELPERS ══════════════════════════════════════════════════════ - function relay(address caller, uint256 msgValue, IFastBridge.BridgeTransaction memory bridgeTx) public { + function relay(address caller, uint256 msgValue, IFastBridgeV2.BridgeTransactionV2 memory bridgeTx) public { bytes memory request = abi.encode(bridgeTx); vm.prank({msgSender: caller, txOrigin: caller}); fastBridge.relay{value: msgValue}(request); @@ -39,7 +39,7 @@ contract FastBridgeV2DstBaseTest is FastBridgeV2Test { address caller, address relayer, uint256 msgValue, - IFastBridge.BridgeTransaction memory bridgeTx + IFastBridgeV2.BridgeTransactionV2 memory bridgeTx ) public { diff --git a/packages/contracts-rfq/test/FastBridgeV2.Dst.Exclusivity.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Dst.Exclusivity.t.sol new file mode 100644 index 0000000000..9d0a414306 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Dst.Exclusivity.t.sol @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2DstTest, IFastBridgeV2} from "./FastBridgeV2.Dst.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2DstExclusivityTest is FastBridgeV2DstTest { + uint256 public constant EXCLUSIVITY_PERIOD = 60 seconds; + + function createFixturesV2() public virtual override { + tokenParamsV2 = IFastBridgeV2.BridgeParamsV2({ + quoteRelayer: relayerA, + quoteExclusivitySeconds: int256(EXCLUSIVITY_PERIOD), + quoteId: "" + }); + ethParamsV2 = IFastBridgeV2.BridgeParamsV2({ + quoteRelayer: relayerB, + quoteExclusivitySeconds: int256(EXCLUSIVITY_PERIOD), + quoteId: "" + }); + + tokenTx.exclusivityRelayer = relayerA; + tokenTx.exclusivityEndTime = block.timestamp + EXCLUSIVITY_PERIOD; + ethTx.exclusivityRelayer = relayerB; + ethTx.exclusivityEndTime = block.timestamp + EXCLUSIVITY_PERIOD; + } + + // ═══════════════════════════════════════════════ RELAY: TOKEN ════════════════════════════════════════════════════ + + // Relayer A has the exclusivity fill rights within the EXCLUSIVITY_PERIOD + + function test_relay_token_exclusivityLastSecond() public { + skip(EXCLUSIVITY_PERIOD); + test_relay_token(); + } + + function test_relay_token_exclusivityOver() public { + skip(EXCLUSIVITY_PERIOD + 1); + test_relay_token(); + } + + function test_relay_token_notQuotedRelayer_revert() public { + vm.expectRevert(ExclusivityPeriodNotPassed.selector); + relay({caller: relayerB, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_token_notQuotedRelayer_exclusivityLastSecond_revert() public { + skip(EXCLUSIVITY_PERIOD); + vm.expectRevert(ExclusivityPeriodNotPassed.selector); + relay({caller: relayerB, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_token_notQuotedRelayer_exclusivityOver() public { + skip(EXCLUSIVITY_PERIOD + 1); + bytes32 txId = getTxId(tokenTx); + expectBridgeRelayed(tokenTx, txId, address(relayerB)); + relay({caller: relayerB, msgValue: 0, bridgeTx: tokenTx}); + assertTrue(fastBridge.bridgeRelays(txId)); + assertEq(dstToken.balanceOf(address(userB)), tokenParams.destAmount); + assertEq(dstToken.balanceOf(address(relayerB)), LEFTOVER_BALANCE); + assertEq(dstToken.balanceOf(address(fastBridge)), 0); + } + + function test_relay_token_withRelayerAddress_exclusivityLastSecond() public { + skip(EXCLUSIVITY_PERIOD); + test_relay_token_withRelayerAddress(); + } + + function test_relay_token_withRelayerAddress_exclusivityOver() public { + skip(EXCLUSIVITY_PERIOD + 1); + test_relay_token_withRelayerAddress(); + } + + function test_relay_token_withRelayerAddress_notQuotedRelayer_revert() public { + vm.expectRevert(ExclusivityPeriodNotPassed.selector); + relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_token_withRelayerAddress_notQuotedRelayer_exclusivityLastSecond_revert() public { + skip(EXCLUSIVITY_PERIOD); + vm.expectRevert(ExclusivityPeriodNotPassed.selector); + relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_token_withRelayerAddress_notQuotedRelayer_exclusivityOver() public { + skip(EXCLUSIVITY_PERIOD + 1); + bytes32 txId = getTxId(tokenTx); + expectBridgeRelayed(tokenTx, txId, address(relayerB)); + relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: 0, bridgeTx: tokenTx}); + assertTrue(fastBridge.bridgeRelays(txId)); + assertEq(dstToken.balanceOf(address(userB)), tokenParams.destAmount); + assertEq(dstToken.balanceOf(address(relayerA)), LEFTOVER_BALANCE); + assertEq(dstToken.balanceOf(address(fastBridge)), 0); + } + + // ════════════════════════════════════════════════ RELAY: ETH ═════════════════════════════════════════════════════ + + // Relayer B has the exclusivity fill rights within the EXCLUSIVITY_PERIOD + + function test_relay_eth_exclusivityLastSecond() public { + skip(EXCLUSIVITY_PERIOD); + test_relay_eth(); + } + + function test_relay_eth_exclusivityOver() public { + skip(EXCLUSIVITY_PERIOD + 1); + test_relay_eth(); + } + + function test_relay_eth_notQuotedRelayer_revert() public { + vm.expectRevert(ExclusivityPeriodNotPassed.selector); + relay({caller: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + } + + function test_relay_eth_notQuotedRelayer_exclusivityLastSecond_revert() public { + skip(EXCLUSIVITY_PERIOD); + vm.expectRevert(ExclusivityPeriodNotPassed.selector); + relay({caller: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + } + + function test_relay_eth_notQuotedRelayer_exclusivityOver() public { + skip(EXCLUSIVITY_PERIOD + 1); + bytes32 txId = getTxId(ethTx); + expectBridgeRelayed(ethTx, txId, address(relayerA)); + relay({caller: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + assertTrue(fastBridge.bridgeRelays(txId)); + assertEq(address(userB).balance, ethParams.destAmount); + assertEq(address(relayerA).balance, LEFTOVER_BALANCE); + assertEq(address(fastBridge).balance, 0); + } + + function test_relay_eth_withRelayerAddress_exclusivityLastSecond() public { + skip(EXCLUSIVITY_PERIOD); + test_relay_eth_withRelayerAddress(); + } + + function test_relay_eth_withRelayerAddress_exclusivityOver() public { + skip(EXCLUSIVITY_PERIOD + 1); + test_relay_eth_withRelayerAddress(); + } + + function test_relay_eth_withRelayerAddress_notQuotedRelayer_revert() public { + vm.expectRevert(ExclusivityPeriodNotPassed.selector); + relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + } + + function test_relay_eth_withRelayerAddress_notQuotedRelayer_exclusivityLastSecond_revert() public { + skip(EXCLUSIVITY_PERIOD); + vm.expectRevert(ExclusivityPeriodNotPassed.selector); + relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + } + + function test_relay_eth_withRelayerAddress_notQuotedRelayer_exclusivityOver() public { + skip(EXCLUSIVITY_PERIOD + 1); + bytes32 txId = getTxId(ethTx); + expectBridgeRelayed(ethTx, txId, address(relayerA)); + relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + assertTrue(fastBridge.bridgeRelays(txId)); + assertEq(address(userB).balance, ethParams.destAmount); + assertEq(address(relayerB).balance, LEFTOVER_BALANCE); + assertEq(address(fastBridge).balance, 0); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol index 3389c1690a..e441a9f3c4 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {ChainIncorrect, DeadlineExceeded, TransactionRelayed, ZeroAddress} from "../contracts/libs/Errors.sol"; - -import {FastBridgeV2DstBaseTest, IFastBridge} from "./FastBridgeV2.Dst.Base.t.sol"; +import {FastBridgeV2DstBaseTest, IFastBridgeV2} from "./FastBridgeV2.Dst.Base.t.sol"; // solhint-disable func-name-mixedcase, ordering contract FastBridgeV2DstTest is FastBridgeV2DstBaseTest { @@ -19,7 +17,13 @@ contract FastBridgeV2DstTest is FastBridgeV2DstBaseTest { uint256 chainGasAmount ); - function expectBridgeRelayed(IFastBridge.BridgeTransaction memory bridgeTx, bytes32 txId, address relayer) public { + function expectBridgeRelayed( + IFastBridgeV2.BridgeTransactionV2 memory bridgeTx, + bytes32 txId, + address relayer + ) + public + { vm.expectEmit(address(fastBridge)); emit BridgeRelayed({ transactionId: txId, @@ -34,67 +38,84 @@ contract FastBridgeV2DstTest is FastBridgeV2DstBaseTest { }); } + function checkRelayedViews(bytes32 txId, address expectedRelayer) public view { + assertTrue(fastBridge.bridgeRelays(txId)); + (uint48 blockNumber, uint48 blockTimestamp, address relayer) = fastBridge.bridgeRelayDetails(txId); + assertEq(blockNumber, block.number); + assertEq(blockTimestamp, block.timestamp); + assertEq(relayer, expectedRelayer); + } + /// @notice RelayerA completes the ERC20 bridge request function test_relay_token() public { bytes32 txId = getTxId(tokenTx); - expectBridgeRelayed(tokenTx, txId, address(relayerA)); + expectBridgeRelayed(tokenTx, txId, relayerA); relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx}); - assertTrue(fastBridge.bridgeRelays(txId)); - assertEq(dstToken.balanceOf(address(userB)), tokenParams.destAmount); - assertEq(dstToken.balanceOf(address(relayerA)), LEFTOVER_BALANCE); + checkRelayedViews({txId: txId, expectedRelayer: relayerA}); + assertEq(dstToken.balanceOf(userB), tokenParams.destAmount); + assertEq(dstToken.balanceOf(relayerA), LEFTOVER_BALANCE); assertEq(dstToken.balanceOf(address(fastBridge)), 0); } - /// @notice RelayerA completes the ERC20 bridge request, using relayerB's address + /// @notice RelayerB completes the ERC20 bridge request, using relayerA's address function test_relay_token_withRelayerAddress() public { bytes32 txId = getTxId(tokenTx); - expectBridgeRelayed(tokenTx, txId, address(relayerB)); - relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: 0, bridgeTx: tokenTx}); - assertTrue(fastBridge.bridgeRelays(txId)); - assertEq(dstToken.balanceOf(address(userB)), tokenParams.destAmount); - assertEq(dstToken.balanceOf(address(relayerA)), LEFTOVER_BALANCE); + expectBridgeRelayed(tokenTx, txId, relayerA); + relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: 0, bridgeTx: tokenTx}); + checkRelayedViews({txId: txId, expectedRelayer: relayerA}); + assertEq(dstToken.balanceOf(userB), tokenParams.destAmount); + assertEq(dstToken.balanceOf(relayerB), LEFTOVER_BALANCE); assertEq(dstToken.balanceOf(address(fastBridge)), 0); } /// @notice RelayerB completes the ETH bridge request function test_relay_eth() public { bytes32 txId = getTxId(ethTx); - expectBridgeRelayed(ethTx, txId, address(relayerB)); + expectBridgeRelayed(ethTx, txId, relayerB); relay({caller: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx}); - assertTrue(fastBridge.bridgeRelays(txId)); - assertEq(address(userB).balance, ethParams.destAmount); - assertEq(address(relayerB).balance, LEFTOVER_BALANCE); + checkRelayedViews({txId: txId, expectedRelayer: relayerB}); + assertEq(userB.balance, ethParams.destAmount); + assertEq(relayerB.balance, LEFTOVER_BALANCE); assertEq(address(fastBridge).balance, 0); } - /// @notice RelayerB completes the ETH bridge request, using relayerA's address + /// @notice RelayerA completes the ETH bridge request, using relayerB's address function test_relay_eth_withRelayerAddress() public { bytes32 txId = getTxId(ethTx); - expectBridgeRelayed(ethTx, txId, address(relayerA)); - relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); - assertTrue(fastBridge.bridgeRelays(txId)); - assertEq(address(userB).balance, ethParams.destAmount); - assertEq(address(relayerB).balance, LEFTOVER_BALANCE); + expectBridgeRelayed(ethTx, txId, relayerB); + relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + checkRelayedViews({txId: txId, expectedRelayer: relayerB}); + assertEq(userB.balance, ethParams.destAmount); + assertEq(relayerA.balance, LEFTOVER_BALANCE); assertEq(address(fastBridge).balance, 0); } - /// @notice RelayerB completes the ETH bridge request, using relayerA's address + /// @notice RelayerA completes the ETH bridge request, using relayerB's address function test_relay_eth_withRelayerAddress_checkBlockData() public { vm.roll(987_654_321); vm.warp(123_456_789); bytes32 txId = getTxId(ethTx); - expectBridgeRelayed(ethTx, txId, address(relayerA)); - relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + expectBridgeRelayed(ethTx, txId, relayerB); + relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx}); assertTrue(fastBridge.bridgeRelays(txId)); - (uint48 recordedBlockNumber, uint48 recordedblockTimestamp,) = fastBridge.bridgeRelayDetails(txId); + (uint48 recordedBlockNumber, uint48 recordedBlockTimestamp, address recordedRelayer) = + fastBridge.bridgeRelayDetails(txId); assertEq(recordedBlockNumber, 987_654_321); - assertEq(recordedblockTimestamp, 123_456_789); - assertEq(address(userB).balance, ethParams.destAmount); - assertEq(address(relayerB).balance, LEFTOVER_BALANCE); + assertEq(recordedBlockTimestamp, 123_456_789); + assertEq(recordedRelayer, relayerB); + assertEq(userB.balance, ethParams.destAmount); + assertEq(relayerA.balance, LEFTOVER_BALANCE); assertEq(address(fastBridge).balance, 0); } // ══════════════════════════════════════════════════ REVERTS ══════════════════════════════════════════════════════ + function test_relay_revert_usedRequestV1() public { + bytes memory request = abi.encode(extractV1(tokenTx)); + vm.expectRevert(); + vm.prank({msgSender: relayerA, txOrigin: relayerA}); + fastBridge.relay(request); + } + function test_relay_revert_chainIncorrect() public { vm.chainId(SRC_CHAIN_ID); vm.expectRevert(ChainIncorrect.selector); @@ -113,6 +134,13 @@ contract FastBridgeV2DstTest is FastBridgeV2DstBaseTest { relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx}); } + function test_relay_withRelayerAddress_revert_usedRequestV1() public { + bytes memory request = abi.encode(extractV1(tokenTx)); + vm.expectRevert(); + vm.prank({msgSender: relayerA, txOrigin: relayerA}); + fastBridge.relay(request, relayerB); + } + function test_relay_withRelayerAddress_revert_chainIncorrect() public { vm.chainId(SRC_CHAIN_ID); vm.expectRevert(ChainIncorrect.selector); diff --git a/packages/contracts-rfq/test/FastBridgeV2.Encoding.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Encoding.t.sol new file mode 100644 index 0000000000..58123fad28 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Encoding.t.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2, FastBridgeV2Test, IFastBridge, IFastBridgeV2} from "./FastBridgeV2.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2EncodingTest is FastBridgeV2Test { + function setUp() public virtual override { + vm.chainId(SRC_CHAIN_ID); + super.setUp(); + } + + function deployFastBridge() public virtual override returns (FastBridgeV2) { + return new FastBridgeV2(address(this)); + } + + function assertEq(IFastBridge.BridgeTransaction memory a, IFastBridge.BridgeTransaction memory b) public pure { + assertEq(a.originChainId, b.originChainId); + assertEq(a.destChainId, b.destChainId); + assertEq(a.originSender, b.originSender); + assertEq(a.destRecipient, b.destRecipient); + assertEq(a.originToken, b.originToken); + assertEq(a.destToken, b.destToken); + assertEq(a.originAmount, b.originAmount); + assertEq(a.destAmount, b.destAmount); + assertEq(a.originFeeAmount, b.originFeeAmount); + assertEq(a.sendChainGas, b.sendChainGas); + assertEq(a.deadline, b.deadline); + assertEq(a.nonce, b.nonce); + } + + function assertEq( + IFastBridgeV2.BridgeTransactionV2 memory a, + IFastBridgeV2.BridgeTransactionV2 memory b + ) + public + pure + { + assertEq(extractV1(a), extractV1(b)); + assertEq(a.exclusivityRelayer, b.exclusivityRelayer); + assertEq(a.exclusivityEndTime, b.exclusivityEndTime); + } + + function test_getBridgeTransaction(IFastBridge.BridgeTransaction memory bridgeTx) public view { + bytes memory request = abi.encode(bridgeTx); + IFastBridge.BridgeTransaction memory decodedTx = fastBridge.getBridgeTransaction(request); + assertEq(decodedTx, bridgeTx); + } + + function test_getBridgeTransaction_supportsV2(IFastBridgeV2.BridgeTransactionV2 memory bridgeTxV2) public view { + bytes memory request = abi.encode(bridgeTxV2); + IFastBridge.BridgeTransaction memory decodedTx = fastBridge.getBridgeTransaction(request); + assertEq(decodedTx, extractV1(bridgeTxV2)); + } + + function test_getBridgeTransactionV2(IFastBridgeV2.BridgeTransactionV2 memory bridgeTxV2) public view { + bytes memory request = abi.encode(bridgeTxV2); + IFastBridgeV2.BridgeTransactionV2 memory decodedTxV2 = fastBridge.getBridgeTransactionV2(request); + assertEq(decodedTxV2, bridgeTxV2); + } + + function test_getBridgeTransactionV2_revert_usedRequestV1(IFastBridge.BridgeTransaction memory bridgeTx) public { + bytes memory request = abi.encode(bridgeTx); + vm.expectRevert(); + fastBridge.getBridgeTransactionV2(request); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.Excl.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.Excl.t.sol new file mode 100644 index 0000000000..dc6b5953f5 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.Excl.t.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2DstGasBenchmarkTest} from "./FastBridgeV2.GasBench.Dst.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2DstExclusivityTest is FastBridgeV2DstGasBenchmarkTest { + uint256 public constant EXCLUSIVITY_PERIOD = 60 seconds; + + function setUp() public virtual override { + super.setUp(); + skip({time: EXCLUSIVITY_PERIOD / 2}); + } + + function createFixturesV2() public virtual override { + tokenParamsV2.quoteRelayer = relayerA; + tokenParamsV2.quoteExclusivitySeconds = int256(EXCLUSIVITY_PERIOD); + ethParamsV2.quoteRelayer = relayerA; + ethParamsV2.quoteExclusivitySeconds = int256(EXCLUSIVITY_PERIOD); + tokenTx.exclusivityRelayer = relayerA; + tokenTx.exclusivityEndTime = block.timestamp + EXCLUSIVITY_PERIOD; + ethTx.exclusivityRelayer = relayerA; + ethTx.exclusivityEndTime = block.timestamp + EXCLUSIVITY_PERIOD; + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol index 42c0cbf7e2..916f465475 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {FastBridgeV2SrcBaseTest, IFastBridge} from "./FastBridgeV2.Src.Base.t.sol"; -import {IFastBridgeV2} from "../contracts/interfaces/IFastBridgeV2.sol"; +import {FastBridgeV2SrcBaseTest, IFastBridgeV2} from "./FastBridgeV2.Src.Base.t.sol"; // solhint-disable func-name-mixedcase, ordering /// @notice This test is used to estimate the gas cost of FastBridgeV2 source chain functions. @@ -10,12 +9,13 @@ import {IFastBridgeV2} from "../contracts/interfaces/IFastBridgeV2.sol"; contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { uint256 public constant BLOCK_TIME = 12 seconds; uint256 public constant INITIAL_RELAYER_BALANCE = 100 ether; + uint256 public constant EXCLUSIVITY_PERIOD = 60 seconds; - IFastBridge.BridgeTransaction public bridgedTokenTx; - IFastBridge.BridgeTransaction public bridgedEthTx; + IFastBridgeV2.BridgeTransactionV2 internal bridgedTokenTx; + IFastBridgeV2.BridgeTransactionV2 internal bridgedEthTx; - IFastBridge.BridgeTransaction public provenTokenTx; - IFastBridge.BridgeTransaction public provenEthTx; + IFastBridgeV2.BridgeTransactionV2 internal provenTokenTx; + IFastBridgeV2.BridgeTransactionV2 internal provenEthTx; uint256 public initialUserBalanceToken; uint256 public initialUserBalanceEth; @@ -47,6 +47,17 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { ethTx.nonce = 4; } + function createFixturesV2() public virtual override { + super.createFixturesV2(); + bridgedTokenTx.exclusivityEndTime = block.timestamp; + provenTokenTx.exclusivityEndTime = block.timestamp; + bridgedEthTx.exclusivityEndTime = block.timestamp; + provenEthTx.exclusivityEndTime = block.timestamp; + // Actual tx will be submitted one block later + tokenTx.exclusivityEndTime = block.timestamp + BLOCK_TIME; + ethTx.exclusivityEndTime = block.timestamp + BLOCK_TIME; + } + function mintTokens() public virtual override { super.mintTokens(); srcToken.mint(relayerA, INITIAL_RELAYER_BALANCE); @@ -92,6 +103,18 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken + tokenParams.originAmount); } + function test_bridge_token_withExclusivity() public { + tokenParamsV2.quoteRelayer = relayerA; + tokenParamsV2.quoteExclusivitySeconds = int256(EXCLUSIVITY_PERIOD); + tokenParamsV2.quoteId = bytes("Created by Relayer A"); + tokenTx.exclusivityRelayer = relayerA; + tokenTx.exclusivityEndTime = block.timestamp + EXCLUSIVITY_PERIOD; + bridge({caller: userA, msgValue: 0, params: tokenParams, paramsV2: tokenParamsV2}); + assertEq(fastBridge.bridgeStatuses(getTxId(tokenTx)), IFastBridgeV2.BridgeStatus.REQUESTED); + assertEq(srcToken.balanceOf(userA), initialUserBalanceToken - tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken + tokenParams.originAmount); + } + function test_prove_token() public { bytes32 txId = getTxId(bridgedTokenTx); prove({caller: relayerA, bridgeTx: bridgedTokenTx, destTxHash: hex"03"}); @@ -161,6 +184,18 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth + ethParams.originAmount); } + function test_bridge_eth_withExclusivity() public { + ethParamsV2.quoteRelayer = relayerA; + ethParamsV2.quoteExclusivitySeconds = int256(EXCLUSIVITY_PERIOD); + ethParamsV2.quoteId = bytes("Created by Relayer A"); + ethTx.exclusivityRelayer = relayerA; + ethTx.exclusivityEndTime = block.timestamp + EXCLUSIVITY_PERIOD; + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams, paramsV2: ethParamsV2}); + assertEq(fastBridge.bridgeStatuses(getTxId(ethTx)), IFastBridgeV2.BridgeStatus.REQUESTED); + assertEq(userA.balance, initialUserBalanceEth - ethParams.originAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth + ethParams.originAmount); + } + function test_prove_eth() public { bytes32 txId = getTxId(bridgedEthTx); prove({caller: relayerA, bridgeTx: bridgedEthTx, destTxHash: hex"03"}); diff --git a/packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol index 9990c371d5..081913d7eb 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol @@ -6,7 +6,9 @@ import {IFastBridgeV2Errors} from "../contracts/interfaces/IFastBridgeV2Errors.s import {FastBridgeTest} from "./FastBridge.t.sol"; // solhint-disable func-name-mixedcase, ordering -contract FastBridgeV2ParityTest is FastBridgeTest, IFastBridgeV2Errors { +/// @notice Contract was updated to be abstract to prevent these tests from being run, +/// as the FastBridgeV2 contract is no longer fully backwards compatible with FastBridge. +abstract contract FastBridgeV2ParityTest is FastBridgeTest, IFastBridgeV2Errors { address public anotherRelayer = makeAddr("Another Relayer"); function deployFastBridge() internal virtual override returns (address) { diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol index 2ce33eca6f..9a45037d54 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {FastBridgeV2, FastBridgeV2Test, IFastBridge} from "./FastBridgeV2.t.sol"; +import {FastBridgeV2, FastBridgeV2Test, IFastBridge, IFastBridgeV2} from "./FastBridgeV2.t.sol"; // solhint-disable func-name-mixedcase, ordering abstract contract FastBridgeV2SrcBaseTest is FastBridgeV2Test { @@ -48,27 +48,39 @@ abstract contract FastBridgeV2SrcBaseTest is FastBridgeV2Test { // ══════════════════════════════════════════════════ HELPERS ══════════════════════════════════════════════════════ - function bridge(address caller, uint256 msgValue, IFastBridge.BridgeParams memory params) public { + function bridge(address caller, uint256 msgValue, IFastBridge.BridgeParams memory params) public virtual { vm.prank({msgSender: caller, txOrigin: caller}); fastBridge.bridge{value: msgValue}(params); } + function bridge( + address caller, + uint256 msgValue, + IFastBridge.BridgeParams memory params, + IFastBridgeV2.BridgeParamsV2 memory paramsV2 + ) + public + { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.bridge{value: msgValue}(params, paramsV2); + } + function prove(address caller, bytes32 transactionId, bytes32 destTxHash, address relayer) public { vm.prank({msgSender: caller, txOrigin: caller}); fastBridge.prove(transactionId, destTxHash, relayer); } - function prove(address caller, IFastBridge.BridgeTransaction memory bridgeTx, bytes32 destTxHash) public { + function prove(address caller, IFastBridgeV2.BridgeTransactionV2 memory bridgeTx, bytes32 destTxHash) public { vm.prank({msgSender: caller, txOrigin: caller}); fastBridge.prove(abi.encode(bridgeTx), destTxHash); } - function claim(address caller, IFastBridge.BridgeTransaction memory bridgeTx) public { + function claim(address caller, IFastBridgeV2.BridgeTransactionV2 memory bridgeTx) public { vm.prank({msgSender: caller, txOrigin: caller}); fastBridge.claim(abi.encode(bridgeTx)); } - function claim(address caller, IFastBridge.BridgeTransaction memory bridgeTx, address to) public { + function claim(address caller, IFastBridgeV2.BridgeTransactionV2 memory bridgeTx, address to) public { vm.prank({msgSender: caller, txOrigin: caller}); fastBridge.claim(abi.encode(bridgeTx), to); } @@ -78,7 +90,7 @@ abstract contract FastBridgeV2SrcBaseTest is FastBridgeV2Test { fastBridge.dispute(txId); } - function refund(address caller, IFastBridge.BridgeTransaction memory bridgeTx) public { + function refund(address caller, IFastBridgeV2.BridgeTransactionV2 memory bridgeTx) public { vm.prank({msgSender: caller, txOrigin: caller}); fastBridge.refund(abi.encode(bridgeTx)); } diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.Exclusivity.Negative.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.Exclusivity.Negative.t.sol new file mode 100644 index 0000000000..0b19f097f3 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.Exclusivity.Negative.t.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2SrcTest, IFastBridge, IFastBridgeV2} from "./FastBridgeV2.Src.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2SrcExclusivityNegativeTest is FastBridgeV2SrcTest { + uint256 public constant EXCLUSIVITY_PERIOD_ABS = 60 seconds; + + function createFixturesV2() public virtual override { + tokenParamsV2.quoteRelayer = relayerA; + tokenParamsV2.quoteExclusivitySeconds = -int256(EXCLUSIVITY_PERIOD_ABS); + tokenParamsV2.quoteId = bytes("Created by Relayer A"); + ethParamsV2.quoteRelayer = relayerB; + ethParamsV2.quoteExclusivitySeconds = -int256(EXCLUSIVITY_PERIOD_ABS); + ethParamsV2.quoteId = bytes("Created by Relayer B"); + + tokenTx.exclusivityRelayer = relayerA; + tokenTx.exclusivityEndTime = block.timestamp - EXCLUSIVITY_PERIOD_ABS; + ethTx.exclusivityRelayer = relayerB; + ethTx.exclusivityEndTime = block.timestamp - EXCLUSIVITY_PERIOD_ABS; + } + + function bridge(address caller, uint256 msgValue, IFastBridge.BridgeParams memory params) public virtual override { + IFastBridgeV2.BridgeParamsV2 memory paramsV2 = params.originToken == ETH_ADDRESS ? ethParamsV2 : tokenParamsV2; + bridge(caller, msgValue, params, paramsV2); + } + + function test_bridge_revert_exclusivityEndTimeZero() public { + tokenParamsV2.quoteExclusivitySeconds = -int256(block.timestamp); + vm.expectRevert(ExclusivityParamsIncorrect.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams, paramsV2: tokenParamsV2}); + } + + function test_bridge_revert_exclusivityPeriodUnderflow() public { + tokenParamsV2.quoteExclusivitySeconds = -int256(block.timestamp + 1); + vm.expectRevert(ExclusivityParamsIncorrect.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams, paramsV2: tokenParamsV2}); + } + + function test_bridge_eth_revert_exclusivityEndTimeZero() public { + ethParamsV2.quoteExclusivitySeconds = -int256(block.timestamp); + vm.expectRevert(ExclusivityParamsIncorrect.selector); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams, paramsV2: ethParamsV2}); + } + + function test_bridge_eth_revert_exclusivityPeriodUnderflow() public { + ethParamsV2.quoteExclusivitySeconds = -int256(block.timestamp + 1); + vm.expectRevert(ExclusivityParamsIncorrect.selector); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams, paramsV2: ethParamsV2}); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.Exclusivity.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.Exclusivity.t.sol new file mode 100644 index 0000000000..259ef0bacd --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.Exclusivity.t.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2SrcTest, IFastBridge, IFastBridgeV2} from "./FastBridgeV2.Src.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2SrcExclusivityTest is FastBridgeV2SrcTest { + uint256 public constant EXCLUSIVITY_PERIOD = 60 seconds; + + function createFixturesV2() public virtual override { + tokenParamsV2.quoteRelayer = relayerA; + tokenParamsV2.quoteExclusivitySeconds = int256(EXCLUSIVITY_PERIOD); + tokenParamsV2.quoteId = bytes("Created by Relayer A"); + ethParamsV2.quoteRelayer = relayerB; + ethParamsV2.quoteExclusivitySeconds = int256(EXCLUSIVITY_PERIOD); + ethParamsV2.quoteId = bytes("Created by Relayer B"); + + tokenTx.exclusivityRelayer = relayerA; + tokenTx.exclusivityEndTime = block.timestamp + EXCLUSIVITY_PERIOD; + ethTx.exclusivityRelayer = relayerB; + ethTx.exclusivityEndTime = block.timestamp + EXCLUSIVITY_PERIOD; + } + + function bridge(address caller, uint256 msgValue, IFastBridge.BridgeParams memory params) public virtual override { + IFastBridgeV2.BridgeParamsV2 memory paramsV2 = params.originToken == ETH_ADDRESS ? ethParamsV2 : tokenParamsV2; + bridge(caller, msgValue, params, paramsV2); + } + + function test_bridge_revert_exclusivityEndTimeOverDeadline() public { + tokenParamsV2.quoteExclusivitySeconds = int256(DEADLINE + 1); + vm.expectRevert(ExclusivityParamsIncorrect.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams, paramsV2: tokenParamsV2}); + } + + function test_bridge_eth_revert_exclusivityEndTimeOverDeadline() public { + ethParamsV2.quoteExclusivitySeconds = int256(DEADLINE + 1); + vm.expectRevert(ExclusivityParamsIncorrect.selector); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams, paramsV2: ethParamsV2}); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol index e0eb6d6ca3..dd01387d72 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {FastBridgeV2SrcBaseTest} from "./FastBridgeV2.Src.Base.t.sol"; -import {IFastBridge, IFastBridgeV2} from "./FastBridgeV2.t.sol"; +import {FastBridgeV2SrcBaseTest, IFastBridge, IFastBridgeV2} from "./FastBridgeV2.Src.Base.t.sol"; // solhint-disable func-name-mixedcase, ordering contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { @@ -28,9 +27,11 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { event BridgeDepositRefunded(bytes32 indexed transactionId, address indexed to, address token, uint256 amount); + event BridgeQuoteDetails(bytes32 indexed transactionId, bytes quoteId); + address public claimTo = makeAddr("Claim To"); - function expectBridgeRequested(IFastBridge.BridgeTransaction memory bridgeTx, bytes32 txId) public { + function expectBridgeRequested(IFastBridgeV2.BridgeTransactionV2 memory bridgeTx, bytes32 txId) public { vm.expectEmit(address(fastBridge)); emit BridgeRequested({ transactionId: txId, @@ -45,13 +46,18 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { }); } + function expectBridgeQuoteDetails(bytes32 txId, bytes memory quoteId) public { + vm.expectEmit(address(fastBridge)); + emit BridgeQuoteDetails({transactionId: txId, quoteId: quoteId}); + } + function expectBridgeProofProvided(bytes32 txId, address relayer, bytes32 destTxHash) public { vm.expectEmit(address(fastBridge)); emit BridgeProofProvided({transactionId: txId, relayer: relayer, transactionHash: destTxHash}); } function expectBridgeDepositClaimed( - IFastBridge.BridgeTransaction memory bridgeTx, + IFastBridgeV2.BridgeTransactionV2 memory bridgeTx, bytes32 txId, address relayer, address to @@ -95,6 +101,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { function test_bridge_token() public { bytes32 txId = getTxId(tokenTx); expectBridgeRequested(tokenTx, txId); + expectBridgeQuoteDetails(txId, tokenParamsV2.quoteId); bridge({caller: userA, msgValue: 0, params: tokenParams}); assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); checkTokenBalancesAfterBridge(userA); @@ -103,6 +110,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { function test_bridge_token_diffSender() public { bytes32 txId = getTxId(tokenTx); expectBridgeRequested(tokenTx, txId); + expectBridgeQuoteDetails(txId, tokenParamsV2.quoteId); bridge({caller: userB, msgValue: 0, params: tokenParams}); assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + tokenParams.originAmount); @@ -120,6 +128,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { bridge({caller: userA, msgValue: 0, params: tokenParams}); bytes32 txId = getTxId(ethTx); expectBridgeRequested(ethTx, txId); + expectBridgeQuoteDetails(txId, ethParamsV2.quoteId); bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); checkEthBalancesAfterBridge(userA); @@ -130,6 +139,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { bridge({caller: userA, msgValue: 0, params: tokenParams}); bytes32 txId = getTxId(ethTx); expectBridgeRequested(ethTx, txId); + expectBridgeQuoteDetails(txId, ethParamsV2.quoteId); bridge({caller: userB, msgValue: ethParams.originAmount, params: ethParams}); assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); assertEq(userA.balance, LEFTOVER_BALANCE + ethParams.originAmount); @@ -145,6 +155,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { ethTx.originSender = userB; bytes32 txId = getTxId(ethTx); expectBridgeRequested(ethTx, txId); + expectBridgeQuoteDetails(txId, ethParamsV2.quoteId); bridge({caller: userB, msgValue: ethParams.originAmount, params: ethParams}); assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); checkEthBalancesAfterBridge(userB); @@ -405,9 +416,10 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { // ═══════════════════════════════════════════════════ CLAIM ═══════════════════════════════════════════════════════ function checkTokenBalancesAfterClaim(address relayer) public view { - assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN + tokenTx.originFeeAmount); + uint256 expectedProtocolFees = INITIAL_PROTOCOL_FEES_TOKEN + tokenTx.originFeeAmount; + assertEq(fastBridge.protocolFees(address(srcToken)), expectedProtocolFees); assertEq(srcToken.balanceOf(relayer), tokenTx.originAmount); - assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenTx.originFeeAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), expectedProtocolFees); } function test_claim_token() public { @@ -470,9 +482,10 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { } function checkEthBalancesAfterClaim(address relayer) public view { - assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH + ethTx.originFeeAmount); + uint256 expectedProtocolFees = INITIAL_PROTOCOL_FEES_ETH + ethTx.originFeeAmount; + assertEq(fastBridge.protocolFees(ETH_ADDRESS), expectedProtocolFees); assertEq(address(relayer).balance, ethTx.originAmount); - assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethTx.originFeeAmount); + assertEq(address(fastBridge).balance, expectedProtocolFees); } function test_claim_eth() public { @@ -768,7 +781,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { refund({caller: refunder, bridgeTx: ethTx}); assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); - assertEq(address(userA).balance, LEFTOVER_BALANCE + ethParams.originAmount); + assertEq(userA.balance, LEFTOVER_BALANCE + ethParams.originAmount); assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); } @@ -782,8 +795,8 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { refund({caller: refunder, bridgeTx: ethTx}); assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); - assertEq(address(userA).balance, LEFTOVER_BALANCE + 2 * ethParams.originAmount); - assertEq(address(userB).balance, LEFTOVER_BALANCE); + assertEq(userA.balance, LEFTOVER_BALANCE + 2 * ethParams.originAmount); + assertEq(userB.balance, LEFTOVER_BALANCE); assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); } @@ -796,7 +809,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { refund({caller: refunder, bridgeTx: ethTx}); assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); - assertEq(address(userA).balance, LEFTOVER_BALANCE + ethParams.originAmount); + assertEq(userA.balance, LEFTOVER_BALANCE + ethParams.originAmount); assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); } @@ -810,7 +823,7 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { refund({caller: caller, bridgeTx: ethTx}); assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); - assertEq(address(userA).balance, LEFTOVER_BALANCE + ethParams.originAmount); + assertEq(userA.balance, LEFTOVER_BALANCE + ethParams.originAmount); assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); } diff --git a/packages/contracts-rfq/test/FastBridgeV2.t.sol b/packages/contracts-rfq/test/FastBridgeV2.t.sol index ab87618149..3828076dac 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.t.sol @@ -15,7 +15,7 @@ import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol" import {Test} from "forge-std/Test.sol"; import {stdStorage, StdStorage} from "forge-std/Test.sol"; -// solhint-disable no-empty-blocks, ordering +// solhint-disable no-empty-blocks, max-states-count, ordering abstract contract FastBridgeV2Test is Test, IFastBridgeV2Errors { using stdStorage for StdStorage; @@ -36,15 +36,19 @@ abstract contract FastBridgeV2Test is Test, IFastBridgeV2Errors { address public governor = makeAddr("Governor"); address public refunder = makeAddr("Refunder"); - IFastBridge.BridgeTransaction public tokenTx; - IFastBridge.BridgeTransaction public ethTx; - IFastBridge.BridgeParams public tokenParams; - IFastBridge.BridgeParams public ethParams; + IFastBridgeV2.BridgeTransactionV2 internal tokenTx; + IFastBridgeV2.BridgeTransactionV2 internal ethTx; + IFastBridge.BridgeParams internal tokenParams; + IFastBridge.BridgeParams internal ethParams; + + IFastBridgeV2.BridgeParamsV2 internal tokenParamsV2; + IFastBridgeV2.BridgeParamsV2 internal ethParamsV2; function setUp() public virtual { srcToken = new MockERC20("SrcToken", 6); dstToken = new MockERC20("DstToken", 6); createFixtures(); + createFixturesV2(); fastBridge = deployFastBridge(); configureFastBridge(); mintTokens(); @@ -80,39 +84,96 @@ abstract contract FastBridgeV2Test is Test, IFastBridgeV2Errors { deadline: block.timestamp + DEADLINE }); - tokenTx = IFastBridge.BridgeTransaction({ - originChainId: SRC_CHAIN_ID, - destChainId: DST_CHAIN_ID, - originSender: userA, - destRecipient: userB, - originToken: address(srcToken), - destToken: address(dstToken), - originAmount: 1e6, - destAmount: 0.99e6, - // override this in tests with protocol fees - originFeeAmount: 0, - sendChainGas: false, - deadline: block.timestamp + DEADLINE, - nonce: 0 - }); - ethTx = IFastBridge.BridgeTransaction({ - originChainId: SRC_CHAIN_ID, - destChainId: DST_CHAIN_ID, - originSender: userA, - destRecipient: userB, - originToken: ETH_ADDRESS, - destToken: ETH_ADDRESS, - originAmount: 1 ether, - destAmount: 0.99 ether, - // override this in tests with protocol fees - originFeeAmount: 0, - sendChainGas: false, - deadline: block.timestamp + DEADLINE, - nonce: 1 - }); + setStorageBridgeTxV2( + tokenTx, + IFastBridge.BridgeTransaction({ + originChainId: SRC_CHAIN_ID, + destChainId: DST_CHAIN_ID, + originSender: userA, + destRecipient: userB, + originToken: address(srcToken), + destToken: address(dstToken), + originAmount: 1e6, + destAmount: 0.99e6, + // override this in tests with protocol fees + originFeeAmount: 0, + sendChainGas: false, + deadline: block.timestamp + DEADLINE, + nonce: 0 + }) + ); + setStorageBridgeTxV2( + ethTx, + IFastBridge.BridgeTransaction({ + originChainId: SRC_CHAIN_ID, + destChainId: DST_CHAIN_ID, + originSender: userA, + destRecipient: userB, + originToken: ETH_ADDRESS, + destToken: ETH_ADDRESS, + originAmount: 1 ether, + destAmount: 0.99 ether, + // override this in tests with protocol fees + originFeeAmount: 0, + sendChainGas: false, + deadline: block.timestamp + DEADLINE, + nonce: 1 + }) + ); + } + + function createFixturesV2() public virtual { + // Override in tests with exclusivity params + tokenParamsV2 = + IFastBridgeV2.BridgeParamsV2({quoteRelayer: address(0), quoteExclusivitySeconds: 0, quoteId: ""}); + ethParamsV2 = IFastBridgeV2.BridgeParamsV2({quoteRelayer: address(0), quoteExclusivitySeconds: 0, quoteId: ""}); + + tokenTx.exclusivityRelayer = address(0); + tokenTx.exclusivityEndTime = block.timestamp; + ethTx.exclusivityRelayer = address(0); + ethTx.exclusivityEndTime = block.timestamp; + } + + function setStorageBridgeTxV2( + IFastBridgeV2.BridgeTransactionV2 storage txV2, + IFastBridge.BridgeTransaction memory txV1 + ) + internal + { + txV2.originChainId = txV1.originChainId; + txV2.destChainId = txV1.destChainId; + txV2.originSender = txV1.originSender; + txV2.destRecipient = txV1.destRecipient; + txV2.originToken = txV1.originToken; + txV2.destToken = txV1.destToken; + txV2.originAmount = txV1.originAmount; + txV2.destAmount = txV1.destAmount; + txV2.originFeeAmount = txV1.originFeeAmount; + txV2.sendChainGas = txV1.sendChainGas; + txV2.deadline = txV1.deadline; + txV2.nonce = txV1.nonce; + } + + function extractV1(IFastBridgeV2.BridgeTransactionV2 memory txV2) + public + pure + returns (IFastBridge.BridgeTransaction memory txV1) + { + txV1.originChainId = txV2.originChainId; + txV1.destChainId = txV2.destChainId; + txV1.originSender = txV2.originSender; + txV1.destRecipient = txV2.destRecipient; + txV1.originToken = txV2.originToken; + txV1.destToken = txV2.destToken; + txV1.originAmount = txV2.originAmount; + txV1.destAmount = txV2.destAmount; + txV1.originFeeAmount = txV2.originFeeAmount; + txV1.sendChainGas = txV2.sendChainGas; + txV1.deadline = txV2.deadline; + txV1.nonce = txV2.nonce; } - function getTxId(IFastBridge.BridgeTransaction memory bridgeTx) public pure returns (bytes32) { + function getTxId(IFastBridgeV2.BridgeTransactionV2 memory bridgeTx) public pure returns (bytes32) { return keccak256(abi.encode(bridgeTx)); } From 2eedb6c37879bed72dbf5ddb745b1dfcc0f636ee Mon Sep 17 00:00:00 2001 From: ChiTimesChi Date: Tue, 1 Oct 2024 16:07:35 +0000 Subject: [PATCH 68/90] Publish - @synapsecns/contracts-rfq@0.6.0 - @synapsecns/synapse-interface@0.40.2 --- packages/contracts-rfq/CHANGELOG.md | 11 +++++++++++ packages/contracts-rfq/package.json | 2 +- packages/synapse-interface/CHANGELOG.md | 8 ++++++++ packages/synapse-interface/package.json | 2 +- 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/contracts-rfq/CHANGELOG.md b/packages/contracts-rfq/CHANGELOG.md index 5b7b105e5a..ee07f57c48 100644 --- a/packages/contracts-rfq/CHANGELOG.md +++ b/packages/contracts-rfq/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.6.0](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.10...@synapsecns/contracts-rfq@0.6.0) (2024-10-01) + + +### Features + +* **contracts-rfq:** relayer exclusivity [SLT-187] ([#3202](https://github.com/synapsecns/sanguine/issues/3202)) ([eb3db7c](https://github.com/synapsecns/sanguine/commit/eb3db7ceb781feaa84f606869d9f9ffee5928cdf)), closes [#3204](https://github.com/synapsecns/sanguine/issues/3204) + + + + + ## [0.5.10](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.9...@synapsecns/contracts-rfq@0.5.10) (2024-10-01) **Note:** Version bump only for package @synapsecns/contracts-rfq diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index 6e12c82fd7..e48b1be68c 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/contracts-rfq", "license": "MIT", - "version": "0.5.10", + "version": "0.6.0", "description": "FastBridge contracts.", "private": true, "files": [ diff --git a/packages/synapse-interface/CHANGELOG.md b/packages/synapse-interface/CHANGELOG.md index 110099bb71..0358d280dc 100644 --- a/packages/synapse-interface/CHANGELOG.md +++ b/packages/synapse-interface/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.40.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.40.1...@synapsecns/synapse-interface@0.40.2) (2024-10-01) + +**Note:** Version bump only for package @synapsecns/synapse-interface + + + + + ## [0.40.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.40.0...@synapsecns/synapse-interface@0.40.1) (2024-09-26) diff --git a/packages/synapse-interface/package.json b/packages/synapse-interface/package.json index ea81ff218a..d789fa45fa 100644 --- a/packages/synapse-interface/package.json +++ b/packages/synapse-interface/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/synapse-interface", - "version": "0.40.1", + "version": "0.40.2", "private": true, "engines": { "node": ">=18.18.0" From 32cee8e3bb88222e9876b0963effb51d72be31a6 Mon Sep 17 00:00:00 2001 From: abtestingalpha <104046418+abtestingalpha@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:44:03 -0400 Subject: [PATCH 69/90] feat(synapse-constants): Refactor with rollup for package build and export [SLT-160] (#3175) * Refactor with rollup for package build and export * New file structure in`src` * Replaces image files with cdn --- packages/synapse-constants/.eslintrc.cjs | 12 + .../constants/assets/chains/arbitrum.svg | 1 - .../constants/assets/chains/aurora.svg | 1 - .../constants/assets/chains/avalanche.svg | 1 - .../constants/assets/chains/base.svg | 1 - .../constants/assets/chains/blast.svg | 4 - .../constants/assets/chains/bnb.svg | 1 - .../constants/assets/chains/boba.svg | 1 - .../constants/assets/chains/canto.svg | 1 - .../constants/assets/chains/cronos.svg | 1 - .../constants/assets/chains/dfk.svg | 1 - .../constants/assets/chains/dogechain.svg | 1 - .../constants/assets/chains/ethereum.svg | 1 - .../constants/assets/chains/fantom.svg | 1 - .../constants/assets/chains/harmony.svg | 1 - .../constants/assets/chains/index.ts | 20 - .../constants/assets/chains/klaytn.svg | 1 - .../constants/assets/chains/linea.svg | 12 - .../constants/assets/chains/metis.svg | 1 - .../constants/assets/chains/moonbeam.svg | 1 - .../constants/assets/chains/moonriver.svg | 1 - .../constants/assets/chains/optimism.svg | 1 - .../constants/assets/chains/polygon.svg | 1 - .../constants/assets/chains/scroll.svg | 9 - .../constants/assets/explorer/arbitrum.svg | 1 - .../constants/assets/explorer/aurora.svg | 1 - .../constants/assets/explorer/avalanche.svg | 1 - .../constants/assets/explorer/basescan.svg | 1 - .../constants/assets/explorer/blast.svg | 4 - .../constants/assets/explorer/boba.svg | 1 - .../constants/assets/explorer/bscscan.svg | 1 - .../constants/assets/explorer/canto.svg | 1 - .../constants/assets/explorer/cronos.svg | 1 - .../constants/assets/explorer/dfk-chain.svg | 1 - .../constants/assets/explorer/dogechain.svg | 1 - .../constants/assets/explorer/etherscan.svg | 1 - .../constants/assets/explorer/fantom.svg | 1 - .../constants/assets/explorer/harmony.svg | 1 - .../constants/assets/explorer/index.ts | 21 - .../constants/assets/explorer/klaytn.svg | 1 - .../constants/assets/explorer/linea.svg | 12 - .../constants/assets/explorer/metis.svg | 1 - .../constants/assets/explorer/moonbeam.svg | 1 - .../constants/assets/explorer/moonriver.svg | 1 - .../constants/assets/explorer/optimism.svg | 1 - .../constants/assets/explorer/polygon.svg | 1 - .../constants/assets/explorer/scroll.svg | 9 - .../constants/assets/icons/ageur.svg | 1 - .../constants/assets/icons/avax.svg | 1 - .../constants/assets/icons/avweth.svg | 1 - .../constants/assets/icons/btc.svg | 1 - .../constants/assets/icons/busd.svg | 1 - .../constants/assets/icons/crvusd.svg | 1 - .../constants/assets/icons/dai.svg | 1 - .../constants/assets/icons/dog.svg | 1 - .../constants/assets/icons/eth.svg | 1 - .../constants/assets/icons/frax.svg | 1 - .../constants/assets/icons/ftm.svg | 1 - .../constants/assets/icons/fusdt.svg | 1 - .../constants/assets/icons/gmx.svg | 1 - .../constants/assets/icons/gohm.svg | 1 - .../constants/assets/icons/h2o.svg | 1 - .../constants/assets/icons/highstreet.svg | 1 - .../constants/assets/icons/hyperjump.svg | 1 - .../constants/assets/icons/index.ts | 44 - .../constants/assets/icons/jewel.png | Bin 2858 -> 0 bytes .../constants/assets/icons/klay.svg | 1 - .../constants/assets/icons/l2dao.svg | 1 - .../constants/assets/icons/link.svg | 1 - .../constants/assets/icons/matic.svg | 1 - .../constants/assets/icons/metamask.svg | 1 - .../constants/assets/icons/mim.svg | 1 - .../constants/assets/icons/movr.svg | 1 - .../constants/assets/icons/neth.svg | 1 - .../constants/assets/icons/newo.svg | 1 - .../constants/assets/icons/nfd.svg | 1 - .../constants/assets/icons/note.svg | 1 - .../constants/assets/icons/nusd.svg | 1 - .../constants/assets/icons/ohm.svg | 1 - .../constants/assets/icons/op.svg | 1 - .../constants/assets/icons/pepe.svg | 1 - .../constants/assets/icons/pls.svg | 1 - .../constants/assets/icons/sdt.svg | 1 - .../constants/assets/icons/sfi.svg | 1 - .../constants/assets/icons/solar.svg | 1 - .../constants/assets/icons/spectral.svg | 5 - .../constants/assets/icons/susd.svg | 1 - .../constants/assets/icons/sushi.svg | 1 - .../constants/assets/icons/syn.svg | 1 - .../constants/assets/icons/unidex.svg | 1 - .../constants/assets/icons/usdb.png | Bin 2234 -> 0 bytes .../constants/assets/icons/usdb.svg | 6 - .../constants/assets/icons/usdc.svg | 1 - .../constants/assets/icons/usdt.svg | 1 - .../constants/assets/icons/vsta.svg | 1 - .../constants/assets/icons/wbtc.svg | 1 - .../constants/assets/icons/weth.svg | 1 - .../constants/assets/index.ts | 3 - .../constants/tokens/bridgeMap.ts | 1999 ----------------- packages/synapse-constants/index.ts | 6 - packages/synapse-constants/package.json | 53 +- packages/synapse-constants/rollup.config.js | 53 + .../{ => src}/constants/bridgeMap.ts | 0 .../{ => src}/constants/chains/index.ts | 2 +- .../{ => src}/constants/chains/master.ts | 178 +- .../{ => src}/constants/index.ts | 3 +- .../{ => src}/constants/minichef.ts | 0 .../{ => src}/constants/tokens/auxilliary.ts | 22 +- .../{ => src}/constants/tokens/bridgeable.ts | 165 +- .../{ => src}/constants/tokens/deprecated.ts | 8 +- .../{ => src}/constants/tokens/index.ts | 4 +- .../{ => src}/constants/tokens/master.ts | 0 .../{ => src}/constants/tokens/poolMaster.ts | 70 +- .../{ => src}/constants/tokens/sushiMaster.ts | 8 +- .../{ => src}/constants/tokens/swapMaster.ts | 8 +- .../synapse-constants/{ => src}/custom.d.ts | 0 packages/synapse-constants/src/index.ts | 6 + .../{ => src}/scripts/abi/IDefaultPool.json | 0 .../{ => src}/scripts/abi/IERC20Metadata.json | 0 .../{ => src}/scripts/abi/SwapQuoter.json | 0 .../{ => src}/scripts/abi/SynapseCCTP.json | 0 .../scripts/abi/SynapseCCTPRouter.json | 0 .../{ => src}/scripts/abi/SynapseRouter.json | 0 .../scripts/data/ignoredBridgeSymbols.json | 0 .../{ => src}/scripts/data/legacyTokens.json | 0 .../{ => src}/scripts/data/providers.json | 0 .../scripts/data/symbolOverrides.json | 0 .../scripts/findMissing.cjs} | 0 .../scripts/generateMaps.cjs} | 4 +- .../scripts/output}/unsupportedTokens.txt | 0 .../scripts/utils/fetchRfqData.cjs} | 0 .../scripts/utils/prettyPrintTs.cjs} | 0 .../types/index.tsx => src/types/index.ts} | 38 +- .../src/utils/makeMultiChainObj.ts | 13 + .../src/utils/validateAddresses.ts | 13 + packages/synapse-constants/tsconfig.json | 27 +- packages/synapse-constants/webpack.config.js | 86 - yarn.lock | 1252 +++-------- 138 files changed, 682 insertions(+), 3580 deletions(-) create mode 100644 packages/synapse-constants/.eslintrc.cjs delete mode 100644 packages/synapse-constants/constants/assets/chains/arbitrum.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/aurora.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/avalanche.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/base.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/blast.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/bnb.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/boba.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/canto.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/cronos.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/dfk.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/dogechain.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/ethereum.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/fantom.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/harmony.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/index.ts delete mode 100644 packages/synapse-constants/constants/assets/chains/klaytn.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/linea.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/metis.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/moonbeam.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/moonriver.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/optimism.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/polygon.svg delete mode 100644 packages/synapse-constants/constants/assets/chains/scroll.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/arbitrum.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/aurora.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/avalanche.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/basescan.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/blast.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/boba.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/bscscan.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/canto.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/cronos.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/dfk-chain.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/dogechain.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/etherscan.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/fantom.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/harmony.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/index.ts delete mode 100644 packages/synapse-constants/constants/assets/explorer/klaytn.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/linea.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/metis.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/moonbeam.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/moonriver.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/optimism.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/polygon.svg delete mode 100644 packages/synapse-constants/constants/assets/explorer/scroll.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/ageur.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/avax.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/avweth.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/btc.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/busd.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/crvusd.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/dai.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/dog.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/eth.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/frax.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/ftm.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/fusdt.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/gmx.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/gohm.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/h2o.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/highstreet.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/hyperjump.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/index.ts delete mode 100644 packages/synapse-constants/constants/assets/icons/jewel.png delete mode 100644 packages/synapse-constants/constants/assets/icons/klay.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/l2dao.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/link.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/matic.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/metamask.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/mim.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/movr.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/neth.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/newo.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/nfd.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/note.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/nusd.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/ohm.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/op.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/pepe.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/pls.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/sdt.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/sfi.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/solar.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/spectral.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/susd.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/sushi.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/syn.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/unidex.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/usdb.png delete mode 100644 packages/synapse-constants/constants/assets/icons/usdb.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/usdc.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/usdt.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/vsta.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/wbtc.svg delete mode 100644 packages/synapse-constants/constants/assets/icons/weth.svg delete mode 100644 packages/synapse-constants/constants/assets/index.ts delete mode 100644 packages/synapse-constants/constants/tokens/bridgeMap.ts delete mode 100644 packages/synapse-constants/index.ts create mode 100644 packages/synapse-constants/rollup.config.js rename packages/synapse-constants/{ => src}/constants/bridgeMap.ts (100%) rename packages/synapse-constants/{ => src}/constants/chains/index.ts (99%) rename packages/synapse-constants/{ => src}/constants/chains/master.ts (69%) rename packages/synapse-constants/{ => src}/constants/index.ts (58%) rename packages/synapse-constants/{ => src}/constants/minichef.ts (100%) rename packages/synapse-constants/{ => src}/constants/tokens/auxilliary.ts (71%) rename packages/synapse-constants/{ => src}/constants/tokens/bridgeable.ts (86%) rename packages/synapse-constants/{ => src}/constants/tokens/deprecated.ts (79%) rename packages/synapse-constants/{ => src}/constants/tokens/index.ts (98%) rename packages/synapse-constants/{ => src}/constants/tokens/master.ts (100%) rename packages/synapse-constants/{ => src}/constants/tokens/poolMaster.ts (90%) rename packages/synapse-constants/{ => src}/constants/tokens/sushiMaster.ts (81%) rename packages/synapse-constants/{ => src}/constants/tokens/swapMaster.ts (84%) rename packages/synapse-constants/{ => src}/custom.d.ts (100%) create mode 100644 packages/synapse-constants/src/index.ts rename packages/synapse-constants/{ => src}/scripts/abi/IDefaultPool.json (100%) rename packages/synapse-constants/{ => src}/scripts/abi/IERC20Metadata.json (100%) rename packages/synapse-constants/{ => src}/scripts/abi/SwapQuoter.json (100%) rename packages/synapse-constants/{ => src}/scripts/abi/SynapseCCTP.json (100%) rename packages/synapse-constants/{ => src}/scripts/abi/SynapseCCTPRouter.json (100%) rename packages/synapse-constants/{ => src}/scripts/abi/SynapseRouter.json (100%) rename packages/synapse-constants/{ => src}/scripts/data/ignoredBridgeSymbols.json (100%) rename packages/synapse-constants/{ => src}/scripts/data/legacyTokens.json (100%) rename packages/synapse-constants/{ => src}/scripts/data/providers.json (100%) rename packages/synapse-constants/{ => src}/scripts/data/symbolOverrides.json (100%) rename packages/synapse-constants/{scripts/findMissing.js => src/scripts/findMissing.cjs} (100%) rename packages/synapse-constants/{scripts/generateMaps.js => src/scripts/generateMaps.cjs} (99%) rename packages/synapse-constants/{ => src/scripts/output}/unsupportedTokens.txt (100%) rename packages/synapse-constants/{scripts/utils/fetchRfqData.js => src/scripts/utils/fetchRfqData.cjs} (100%) rename packages/synapse-constants/{scripts/utils/prettyPrintTs.js => src/scripts/utils/prettyPrintTs.cjs} (100%) rename packages/synapse-constants/{constants/types/index.tsx => src/types/index.ts} (92%) create mode 100644 packages/synapse-constants/src/utils/makeMultiChainObj.ts create mode 100644 packages/synapse-constants/src/utils/validateAddresses.ts delete mode 100644 packages/synapse-constants/webpack.config.js diff --git a/packages/synapse-constants/.eslintrc.cjs b/packages/synapse-constants/.eslintrc.cjs new file mode 100644 index 0000000000..32e72b00e0 --- /dev/null +++ b/packages/synapse-constants/.eslintrc.cjs @@ -0,0 +1,12 @@ +module.exports = { + extends: '../../.eslintrc.js', + overrides: [ + { + files: ['**/*.ts'], + rules: { + 'guard-for-in': 'off', + 'prefer-arrow/prefer-arrow-functions': 'off', + }, + }, + ], +} diff --git a/packages/synapse-constants/constants/assets/chains/arbitrum.svg b/packages/synapse-constants/constants/assets/chains/arbitrum.svg deleted file mode 100644 index cb285a937a..0000000000 --- a/packages/synapse-constants/constants/assets/chains/arbitrum.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/aurora.svg b/packages/synapse-constants/constants/assets/chains/aurora.svg deleted file mode 100644 index 5b5441b3dd..0000000000 --- a/packages/synapse-constants/constants/assets/chains/aurora.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/avalanche.svg b/packages/synapse-constants/constants/assets/chains/avalanche.svg deleted file mode 100644 index 5695785b66..0000000000 --- a/packages/synapse-constants/constants/assets/chains/avalanche.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/base.svg b/packages/synapse-constants/constants/assets/chains/base.svg deleted file mode 100644 index 5dd1e67af0..0000000000 --- a/packages/synapse-constants/constants/assets/chains/base.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/blast.svg b/packages/synapse-constants/constants/assets/chains/blast.svg deleted file mode 100644 index 5df8a7f017..0000000000 --- a/packages/synapse-constants/constants/assets/chains/blast.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/synapse-constants/constants/assets/chains/bnb.svg b/packages/synapse-constants/constants/assets/chains/bnb.svg deleted file mode 100644 index 4279646a3d..0000000000 --- a/packages/synapse-constants/constants/assets/chains/bnb.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/boba.svg b/packages/synapse-constants/constants/assets/chains/boba.svg deleted file mode 100644 index 6f3f19fdfa..0000000000 --- a/packages/synapse-constants/constants/assets/chains/boba.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/canto.svg b/packages/synapse-constants/constants/assets/chains/canto.svg deleted file mode 100644 index 20e0fe2177..0000000000 --- a/packages/synapse-constants/constants/assets/chains/canto.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/cronos.svg b/packages/synapse-constants/constants/assets/chains/cronos.svg deleted file mode 100644 index f2c429fc7f..0000000000 --- a/packages/synapse-constants/constants/assets/chains/cronos.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/dfk.svg b/packages/synapse-constants/constants/assets/chains/dfk.svg deleted file mode 100644 index 7daf4cc71e..0000000000 --- a/packages/synapse-constants/constants/assets/chains/dfk.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/dogechain.svg b/packages/synapse-constants/constants/assets/chains/dogechain.svg deleted file mode 100644 index 9c8a52c0cb..0000000000 --- a/packages/synapse-constants/constants/assets/chains/dogechain.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/ethereum.svg b/packages/synapse-constants/constants/assets/chains/ethereum.svg deleted file mode 100644 index 407dcd3072..0000000000 --- a/packages/synapse-constants/constants/assets/chains/ethereum.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/fantom.svg b/packages/synapse-constants/constants/assets/chains/fantom.svg deleted file mode 100644 index 3e23ff5c0e..0000000000 --- a/packages/synapse-constants/constants/assets/chains/fantom.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/harmony.svg b/packages/synapse-constants/constants/assets/chains/harmony.svg deleted file mode 100644 index 4ce9dc2a07..0000000000 --- a/packages/synapse-constants/constants/assets/chains/harmony.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/index.ts b/packages/synapse-constants/constants/assets/chains/index.ts deleted file mode 100644 index ea0e9e1205..0000000000 --- a/packages/synapse-constants/constants/assets/chains/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -export * as arbitrumImg from './arbitrum.svg' -export * as auroraImg from './aurora.svg' -export * as avalancheImg from './avalanche.svg' -export * as baseImg from './base.svg' -export * as bobaImg from './boba.svg' -export * as bscImg from './bnb.svg' -export * as cantoImg from './canto.svg' -export * as cronosImg from './cronos.svg' -export * as dfkImg from './dfk.svg' -export * as dogechainImg from './dogechain.svg' -export * as ethImg from './ethereum.svg' -export * as fantomImg from './fantom.svg' -export * as harmonyImg from './harmony.svg' -export * as klaytnImg from './klaytn.svg' -export * as metisImg from './metis.svg' -export * as moonbeamImg from './moonbeam.svg' -export * as moonriverImg from './moonriver.svg' -export * as optimismImg from './optimism.svg' -export * as polygonImg from './polygon.svg' -export * as lineaImg from './linea.svg' diff --git a/packages/synapse-constants/constants/assets/chains/klaytn.svg b/packages/synapse-constants/constants/assets/chains/klaytn.svg deleted file mode 100644 index 88efdb79b5..0000000000 --- a/packages/synapse-constants/constants/assets/chains/klaytn.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/linea.svg b/packages/synapse-constants/constants/assets/chains/linea.svg deleted file mode 100644 index 8639b02212..0000000000 --- a/packages/synapse-constants/constants/assets/chains/linea.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/synapse-constants/constants/assets/chains/metis.svg b/packages/synapse-constants/constants/assets/chains/metis.svg deleted file mode 100644 index fd1342a2c0..0000000000 --- a/packages/synapse-constants/constants/assets/chains/metis.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/moonbeam.svg b/packages/synapse-constants/constants/assets/chains/moonbeam.svg deleted file mode 100644 index 0cbeda36a7..0000000000 --- a/packages/synapse-constants/constants/assets/chains/moonbeam.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/moonriver.svg b/packages/synapse-constants/constants/assets/chains/moonriver.svg deleted file mode 100644 index 07cf4ddfcd..0000000000 --- a/packages/synapse-constants/constants/assets/chains/moonriver.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/optimism.svg b/packages/synapse-constants/constants/assets/chains/optimism.svg deleted file mode 100644 index 7378f33b2b..0000000000 --- a/packages/synapse-constants/constants/assets/chains/optimism.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/polygon.svg b/packages/synapse-constants/constants/assets/chains/polygon.svg deleted file mode 100644 index 879ae5a82c..0000000000 --- a/packages/synapse-constants/constants/assets/chains/polygon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/scroll.svg b/packages/synapse-constants/constants/assets/chains/scroll.svg deleted file mode 100644 index fbfa194347..0000000000 --- a/packages/synapse-constants/constants/assets/chains/scroll.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/packages/synapse-constants/constants/assets/explorer/arbitrum.svg b/packages/synapse-constants/constants/assets/explorer/arbitrum.svg deleted file mode 100644 index 24c3ecc168..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/arbitrum.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/aurora.svg b/packages/synapse-constants/constants/assets/explorer/aurora.svg deleted file mode 100644 index 4a8683c6ee..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/aurora.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/avalanche.svg b/packages/synapse-constants/constants/assets/explorer/avalanche.svg deleted file mode 100644 index 7b3a778c54..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/avalanche.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/basescan.svg b/packages/synapse-constants/constants/assets/explorer/basescan.svg deleted file mode 100644 index fdefbf2fee..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/basescan.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/blast.svg b/packages/synapse-constants/constants/assets/explorer/blast.svg deleted file mode 100644 index 5df8a7f017..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/blast.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/synapse-constants/constants/assets/explorer/boba.svg b/packages/synapse-constants/constants/assets/explorer/boba.svg deleted file mode 100644 index a39fb90aac..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/boba.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/bscscan.svg b/packages/synapse-constants/constants/assets/explorer/bscscan.svg deleted file mode 100644 index a96d66873d..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/bscscan.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/canto.svg b/packages/synapse-constants/constants/assets/explorer/canto.svg deleted file mode 100644 index 4032a81b40..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/canto.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/cronos.svg b/packages/synapse-constants/constants/assets/explorer/cronos.svg deleted file mode 100644 index d35b790126..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/cronos.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/dfk-chain.svg b/packages/synapse-constants/constants/assets/explorer/dfk-chain.svg deleted file mode 100644 index ea73403433..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/dfk-chain.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/dogechain.svg b/packages/synapse-constants/constants/assets/explorer/dogechain.svg deleted file mode 100644 index 9c8a52c0cb..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/dogechain.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/etherscan.svg b/packages/synapse-constants/constants/assets/explorer/etherscan.svg deleted file mode 100644 index a826445d2d..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/etherscan.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/fantom.svg b/packages/synapse-constants/constants/assets/explorer/fantom.svg deleted file mode 100644 index 7984eaf5ac..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/fantom.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/harmony.svg b/packages/synapse-constants/constants/assets/explorer/harmony.svg deleted file mode 100644 index 6b75928be0..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/harmony.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/index.ts b/packages/synapse-constants/constants/assets/explorer/index.ts deleted file mode 100644 index c826a27ea9..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -export * as ethExplorerImg from './etherscan.svg' -export * as arbitrumExplorerImg from './arbitrum.svg' -export * as bnbExplorerImg from './bscscan.svg' -export * as avalancheExplorerImg from './avalanche.svg' -export * as cantoExplorerImg from './canto.svg' -export * as optimismExplorerImg from './optimism.svg' -export * as polygonExplorerImg from './polygon.svg' -export * as dfkExplorerImg from './dfk-chain.svg' -export * as klaytynExplorerImg from './klaytn.svg' -export * as fantomExplorerImg from './fantom.svg' -export * as cronosExplorerImg from './cronos.svg' -export * as bobaExplorerImg from './boba.svg' -export * as metisExplorerImg from './metis.svg' -export * as auroraExplorerImg from './aurora.svg' -export * as harmonyExplorerImg from './harmony.svg' -export * as moonbeamExplorerImg from './moonbeam.svg' -export * as moonriverExplorerImg from './moonriver.svg' -export * as dogeExplorerImg from './dogechain.svg' -export * as baseExplorerImg from './basescan.svg' -export * as scrollExplorerImg from './scroll.svg' -export * as lineaExplorerImg from './linea.svg' diff --git a/packages/synapse-constants/constants/assets/explorer/klaytn.svg b/packages/synapse-constants/constants/assets/explorer/klaytn.svg deleted file mode 100644 index cb07c44c78..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/klaytn.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/linea.svg b/packages/synapse-constants/constants/assets/explorer/linea.svg deleted file mode 100644 index 8639b02212..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/linea.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/synapse-constants/constants/assets/explorer/metis.svg b/packages/synapse-constants/constants/assets/explorer/metis.svg deleted file mode 100644 index fd1342a2c0..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/metis.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/moonbeam.svg b/packages/synapse-constants/constants/assets/explorer/moonbeam.svg deleted file mode 100644 index cc65830b75..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/moonbeam.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/moonriver.svg b/packages/synapse-constants/constants/assets/explorer/moonriver.svg deleted file mode 100644 index 94984f7ba0..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/moonriver.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/optimism.svg b/packages/synapse-constants/constants/assets/explorer/optimism.svg deleted file mode 100644 index 6650dfa669..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/optimism.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/polygon.svg b/packages/synapse-constants/constants/assets/explorer/polygon.svg deleted file mode 100644 index a3a486eb43..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/polygon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/scroll.svg b/packages/synapse-constants/constants/assets/explorer/scroll.svg deleted file mode 100644 index fbfa194347..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/scroll.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/packages/synapse-constants/constants/assets/icons/ageur.svg b/packages/synapse-constants/constants/assets/icons/ageur.svg deleted file mode 100644 index e48ff825bc..0000000000 --- a/packages/synapse-constants/constants/assets/icons/ageur.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/avax.svg b/packages/synapse-constants/constants/assets/icons/avax.svg deleted file mode 100644 index 5695785b66..0000000000 --- a/packages/synapse-constants/constants/assets/icons/avax.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/avweth.svg b/packages/synapse-constants/constants/assets/icons/avweth.svg deleted file mode 100644 index 8e9d055972..0000000000 --- a/packages/synapse-constants/constants/assets/icons/avweth.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/btc.svg b/packages/synapse-constants/constants/assets/icons/btc.svg deleted file mode 100644 index a10e2bcc2c..0000000000 --- a/packages/synapse-constants/constants/assets/icons/btc.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/busd.svg b/packages/synapse-constants/constants/assets/icons/busd.svg deleted file mode 100644 index 939021a69d..0000000000 --- a/packages/synapse-constants/constants/assets/icons/busd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/crvusd.svg b/packages/synapse-constants/constants/assets/icons/crvusd.svg deleted file mode 100644 index de40a549fc..0000000000 --- a/packages/synapse-constants/constants/assets/icons/crvusd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/dai.svg b/packages/synapse-constants/constants/assets/icons/dai.svg deleted file mode 100644 index 41d6d21513..0000000000 --- a/packages/synapse-constants/constants/assets/icons/dai.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/dog.svg b/packages/synapse-constants/constants/assets/icons/dog.svg deleted file mode 100644 index 1d7640718a..0000000000 --- a/packages/synapse-constants/constants/assets/icons/dog.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/eth.svg b/packages/synapse-constants/constants/assets/icons/eth.svg deleted file mode 100644 index e9dcc7f0c3..0000000000 --- a/packages/synapse-constants/constants/assets/icons/eth.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/frax.svg b/packages/synapse-constants/constants/assets/icons/frax.svg deleted file mode 100644 index cf8c89f20d..0000000000 --- a/packages/synapse-constants/constants/assets/icons/frax.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/ftm.svg b/packages/synapse-constants/constants/assets/icons/ftm.svg deleted file mode 100644 index 81b17f14c6..0000000000 --- a/packages/synapse-constants/constants/assets/icons/ftm.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/fusdt.svg b/packages/synapse-constants/constants/assets/icons/fusdt.svg deleted file mode 100644 index 5d813d5824..0000000000 --- a/packages/synapse-constants/constants/assets/icons/fusdt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/gmx.svg b/packages/synapse-constants/constants/assets/icons/gmx.svg deleted file mode 100644 index c1534ad171..0000000000 --- a/packages/synapse-constants/constants/assets/icons/gmx.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/gohm.svg b/packages/synapse-constants/constants/assets/icons/gohm.svg deleted file mode 100644 index 7e4842d855..0000000000 --- a/packages/synapse-constants/constants/assets/icons/gohm.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/h2o.svg b/packages/synapse-constants/constants/assets/icons/h2o.svg deleted file mode 100644 index a2935082de..0000000000 --- a/packages/synapse-constants/constants/assets/icons/h2o.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/highstreet.svg b/packages/synapse-constants/constants/assets/icons/highstreet.svg deleted file mode 100644 index eb53b0ea3e..0000000000 --- a/packages/synapse-constants/constants/assets/icons/highstreet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/hyperjump.svg b/packages/synapse-constants/constants/assets/icons/hyperjump.svg deleted file mode 100644 index 5f8ca538f5..0000000000 --- a/packages/synapse-constants/constants/assets/icons/hyperjump.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/index.ts b/packages/synapse-constants/constants/assets/icons/index.ts deleted file mode 100644 index 540b13d226..0000000000 --- a/packages/synapse-constants/constants/assets/icons/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -export * as ageurLogo from './ageur.svg' -export * as avaxLogo from './avax.svg' -export * as btcLogo from './btc.svg' -export * as busdLogo from './busd.svg' -export * as crvusdLogo from './crvusd.svg' -export * as linkLogo from './link.svg' -export * as daiLogo from './dai.svg' -export * as dogLogo from './dog.svg' -export * as ethLogo from './eth.svg' -export * as fraxLogo from './frax.svg' -export * as ftmLogo from './ftm.svg' -export * as gmxLogo from './gmx.svg' -export * as h2oLogo from './h2o.svg' -export * as highLogo from './highstreet.svg' -export * as hyperjumpLogo from './hyperjump.svg' -export * as jewelLogo from './jewel.png' -export * as klayLogo from './klay.svg' -export * as l2daoLogo from './l2dao.svg' -export * as maticLogo from './matic.svg' -export * as movrLogo from './movr.svg' -export * as nethLogo from './neth.svg' -export * as newoLogo from './newo.svg' -export * as nfdLogo from './nfd.svg' -export * as noteLogo from './note.svg' -export * as nusdLogo from './nusd.svg' -export * as ohmLogo from './ohm.svg' -export * as pepeLogo from './pepe.svg' -export * as plsLogo from './pls.svg' -export * as sdtLogo from './sdt.svg' -export * as sfiLogo from './sfi.svg' -export * as solarbeamLogo from './solar.svg' -export * as susdLogo from './susd.svg' -export * as synapseLogo from './syn.svg' -export * as unidexLogo from './unidex.svg' -export * as vstaLogo from './vsta.svg' -export * as wbtcLogo from './wbtc.svg' -export * as wethLogo from './weth.svg' -export * as avwethLogo from './avweth.svg' -export * as mimLogo from './mim.svg' -export * as usdcLogo from './usdc.svg' -export * as usdtLogo from './usdt.svg' -export * as usdbLogo from './usdc.svg' -export * as fusdtLogo from './usdt.svg' -export * as sushiLogo from './sushi.svg' diff --git a/packages/synapse-constants/constants/assets/icons/jewel.png b/packages/synapse-constants/constants/assets/icons/jewel.png deleted file mode 100644 index 999418cf802e4454360f18f9c3c6c146614cba0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2858 zcmV+_3)S?AP)kPore^YCW->G4!m4aGOGH#e*#xnb00J&VmbfQI3?Rfv5ETgmF^UT! zDB^;MxPXd)f&@u|hz6K zWCTpIIu&e$l9*{~>2!!O;(ax&M!n5Z6k%#qeQZ8Z9|1H(0*}L0)6;Fh!%fJJFnxOv z1>pi=BwGtjgL=(+;59Fr>X^+dnv$^k0Z10`O#9AP4U;}H2$^EK&i4|;1i0rlH} zj!dAxOsYbuF21G;2Y>tQF!0MV^c1?|dd!pu`+PKnZ*1quyyR~1VEMbw0)3f4?@={% z8fZ;rRp1c*zcWN(xQP|tc{1O52IxyidwneKrMaOscbQ76Nq1*+pf3YxJ9GajvSi zWHjT1QULl+QNs@$faDmZJJgnZ9bO5!h*cvhyK2JGRU3}}u0vRL-BJc1^Jx&8d(XbI z3XMp{z4l~8-C86A;Be3w1JXI$ZwJvDN+VX)sH6p?=9Aducb;uQNU6OHM7<<~TI3b& zlY(k-s*HJ;lLB1Ib_A-{69jG0h$2>%sO08Ru@=o8@%XZMrVK#hLm$-i?im)KQWA%t z8*^j;<{qAm#zN@^WqZ)vw1$F+Jqa{MDvsDg%O|}uD!Y4@%~C~)HiE7VyfKfvz+J}lU4G$T*TIwZbn+D0KS%k0IVda zji9?knW$WmH5+1NC^YkMU^j9kzRY}S&t^3BAEQG$qU*q8DJZ2Dnf$WY3UT)qDg-#( z;02dh1G$LJOISb`H{}2U#nveMEe!YiV$g-lGCBWfU@wZg197(}7EJ@kxp`%}*ZVzg zNuoGJ)Lj>ubCmeR3nyDW5moJs&E>Pns1g88Dif0_bt_pK^`RBwft$SNN1{{BKDZ!Wjk^B*?C*m15Im8jOOq=X zg7XG2&o+R;IX!5e)@E|e5ex!cbyFKaP6R^9G7{sz_U+;3S^M?x!?pGxq%?V=tuM0p|YjhrXy8zVhm!5w?#@QJt>Ify{ssWJC zy3KD7pyIb^);bkE(eyWgkZJV*Zm&RItks(bptvOS)Gl;rbK5qMVRZrcZN!@d;D`2- z_zN=66X*{INO<@ui_qv>u4rtjg}AE<4M>tRFEvKy`JtusQDpw;GcV-Ue}hs{EGDGr zuqf5foTx5<{DmyJ-kaUPcG@r3yXIcVB>ikEI(n6fa}(ndq|)b>c}K_+`(*?Y3ARm&5h^f$Di!veOVVrRK?`iN6*Du-mF5W1gyP zvPuU@2W-)q=Zd^-rW~N=h%J)WXbu9zF4IMOu9WbQ5RJE@f&NU0e*F&BnM=?i-iyps zYq)%`hs9Z@n0ItC?8CGm{B{D$FV92z(U~ycIUdHT6ESO_A$(5Rpsp<)neCnmmD9W) zt`U=v7!>PKI|>>U=y`u&Z7r$dLZY|x723BBva_7=~7!Ml z3gw;4R)RwmMOjcg@pm#L+8rvZbF|dV&o%_W4)~^@=h6j;j1DW^G#z-Jn2O*W2Rf)^ zp0(b%h1zmaIdovD=K~!zUYh?inO8o%KmLmqoe8P}gUDIRC(V(Twlm)q=a0|EmuWLN z#JXZDx_T-Fn3EuY$H}SO{PZ1@k+^%7TvU?z9ZPgy9@x?15u4|gkV1qy{}TjJ68$kR zE%jw}qBS}S-5JE<14alfvq5mFH9f=9{mZa&zbs(YT9G#$zGr7*>Q-YMPO?`Nl{{^{ z^gh6x5B_AtjlC{Jn9E0Cm54Nj=o~w26l+v&9sRs|`KN;QV^h&{(HJgQ20 zRH`|YN9=Yp-cBk=c~q)Dlt*l7j0GwZ?QrUAbCpyaHyykDCaMsX>J1*TS)s<*?x{@% zRMIvNO~fo2ho$;FD&O7^PI$*^;n?>u@1u9s()@qFt5#J0A8jbAdYfZ~pa1{>07*qo IM6N<$f|`tLVE_OC diff --git a/packages/synapse-constants/constants/assets/icons/klay.svg b/packages/synapse-constants/constants/assets/icons/klay.svg deleted file mode 100644 index 552e0a6e50..0000000000 --- a/packages/synapse-constants/constants/assets/icons/klay.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/synapse-constants/constants/assets/icons/l2dao.svg b/packages/synapse-constants/constants/assets/icons/l2dao.svg deleted file mode 100644 index 22175d2cb7..0000000000 --- a/packages/synapse-constants/constants/assets/icons/l2dao.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/link.svg b/packages/synapse-constants/constants/assets/icons/link.svg deleted file mode 100644 index cc848c331c..0000000000 --- a/packages/synapse-constants/constants/assets/icons/link.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/matic.svg b/packages/synapse-constants/constants/assets/icons/matic.svg deleted file mode 100644 index 879ae5a82c..0000000000 --- a/packages/synapse-constants/constants/assets/icons/matic.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/metamask.svg b/packages/synapse-constants/constants/assets/icons/metamask.svg deleted file mode 100644 index fa6d5b322b..0000000000 --- a/packages/synapse-constants/constants/assets/icons/metamask.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/mim.svg b/packages/synapse-constants/constants/assets/icons/mim.svg deleted file mode 100644 index 6397e92bae..0000000000 --- a/packages/synapse-constants/constants/assets/icons/mim.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/movr.svg b/packages/synapse-constants/constants/assets/icons/movr.svg deleted file mode 100644 index 07cf4ddfcd..0000000000 --- a/packages/synapse-constants/constants/assets/icons/movr.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/neth.svg b/packages/synapse-constants/constants/assets/icons/neth.svg deleted file mode 100644 index 913868f284..0000000000 --- a/packages/synapse-constants/constants/assets/icons/neth.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/newo.svg b/packages/synapse-constants/constants/assets/icons/newo.svg deleted file mode 100644 index 444ed2965f..0000000000 --- a/packages/synapse-constants/constants/assets/icons/newo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/nfd.svg b/packages/synapse-constants/constants/assets/icons/nfd.svg deleted file mode 100644 index 7534a91235..0000000000 --- a/packages/synapse-constants/constants/assets/icons/nfd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/note.svg b/packages/synapse-constants/constants/assets/icons/note.svg deleted file mode 100644 index c1be5c5dc9..0000000000 --- a/packages/synapse-constants/constants/assets/icons/note.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/nusd.svg b/packages/synapse-constants/constants/assets/icons/nusd.svg deleted file mode 100644 index 191a497c9e..0000000000 --- a/packages/synapse-constants/constants/assets/icons/nusd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/ohm.svg b/packages/synapse-constants/constants/assets/icons/ohm.svg deleted file mode 100644 index decefe65b8..0000000000 --- a/packages/synapse-constants/constants/assets/icons/ohm.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/op.svg b/packages/synapse-constants/constants/assets/icons/op.svg deleted file mode 100644 index bb5f2a1511..0000000000 --- a/packages/synapse-constants/constants/assets/icons/op.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/pepe.svg b/packages/synapse-constants/constants/assets/icons/pepe.svg deleted file mode 100644 index f08be7d1d6..0000000000 --- a/packages/synapse-constants/constants/assets/icons/pepe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/pls.svg b/packages/synapse-constants/constants/assets/icons/pls.svg deleted file mode 100644 index fccdbcd2ca..0000000000 --- a/packages/synapse-constants/constants/assets/icons/pls.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/sdt.svg b/packages/synapse-constants/constants/assets/icons/sdt.svg deleted file mode 100644 index 0d0824c16c..0000000000 --- a/packages/synapse-constants/constants/assets/icons/sdt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/sfi.svg b/packages/synapse-constants/constants/assets/icons/sfi.svg deleted file mode 100644 index e1a8cd0ad2..0000000000 --- a/packages/synapse-constants/constants/assets/icons/sfi.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/solar.svg b/packages/synapse-constants/constants/assets/icons/solar.svg deleted file mode 100644 index ed386e2dfa..0000000000 --- a/packages/synapse-constants/constants/assets/icons/solar.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/spectral.svg b/packages/synapse-constants/constants/assets/icons/spectral.svg deleted file mode 100644 index 86daa939ae..0000000000 --- a/packages/synapse-constants/constants/assets/icons/spectral.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/packages/synapse-constants/constants/assets/icons/susd.svg b/packages/synapse-constants/constants/assets/icons/susd.svg deleted file mode 100644 index 924acb5405..0000000000 --- a/packages/synapse-constants/constants/assets/icons/susd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/sushi.svg b/packages/synapse-constants/constants/assets/icons/sushi.svg deleted file mode 100644 index bb4bc618fe..0000000000 --- a/packages/synapse-constants/constants/assets/icons/sushi.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/syn.svg b/packages/synapse-constants/constants/assets/icons/syn.svg deleted file mode 100644 index f7a230124d..0000000000 --- a/packages/synapse-constants/constants/assets/icons/syn.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/unidex.svg b/packages/synapse-constants/constants/assets/icons/unidex.svg deleted file mode 100644 index cf432ca520..0000000000 --- a/packages/synapse-constants/constants/assets/icons/unidex.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/usdb.png b/packages/synapse-constants/constants/assets/icons/usdb.png deleted file mode 100644 index 5e432e8e86eaefabb1e8b6058088de0d6c534b19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2234 zcmV;r2u1gaP)02DGnQELDYE)gb02O>fR z95?^|{r~^{3MEDgA3#M{a!6Ni5+*?g9XS;#Mn+h21syv{S#$>-JqR2)OIK(B5-SZP zN8#n^01_?#{`pB-b4XZm8!k*4E=da?K1*72NLO(mGEF>8WEw0-4Jx?t@QS$cr z02VbeLRb9z_*P_cJyC2&R&pILOhs38N?CC&Jy<6@R}3CMMp$wB`}+L+{POkp{r&k2 zBS!D?_Cr>16emImA3gj1`0nxZNmgv?@9{=fZwVts2_i%W8adeC<6vxk1RFaC8Z_|q z_5T0-{QCJmQEC7WEJa&!{r&vh;pOJ(?K47E01`A7D@g?#J3UfwFF{=vCq*4HQ2hP( zB{@|aElEIBZbDOSMp|@ATX6&(J`f~C01zoRNnHt zTR~KCB|cacB|slEPA58BM_hD5Qf3=5PXHZ1H%DDZSZC|(@Smou2_;DT{r!`lt2s(t z87e`Jn4furk=5JakeH=IP-`qfU-tOQsCw42qHXIWOqPSa$#+GXmNwV z$IL`gUj`U2`~CGLHcdB7Y6~PzGDBfHPH$Uic_=wdP+Dktf{W(o=o>6RBQZo|aDO^U zU|(*2L{MQnM_Wx>a@*kJ_WJxMHBZRR)lpq+2p&K`QEcAf=3HiZbAE{W`So{%lU!zY z+uY&B%g>LLqQ}nBb%T6&g^0nV*mgJcu7P-RCr$PmQ#== z*&D>`l&)>tYTLGL+dZ^x+qP|EZQC~fjIEi8jhUyqw|ieZpDTagS6OjRCFE5j8WBn* zg#H6N!%r%ejT<@w!w{q~yuCYZU*(M(mHT8XsA4f~XaEckSE{bvngIyizf$N&Wey2| zKy0>IviBxP4+Vge4t~8_7KRK3kfUS6$(#DYB=E=e>%W1YK*##DwL<}H3=dPkymOkf zcWkgkHvX6XiEZ1@OJE=K(;+zs}vaVWXy$r*589dR_)UOKJ2aKb)}%Bg z#Ty{JU9zcnVaSYfKN#Dn*sx&Hq6IENW4KaM@=Qp@g!Kj($8yO0l)lpo$_E>n4$7=+n%pRvq*xZ%ANzEUys+Lgg;NXBu2}{ zak@69sU&QC)65Hmk4oRn9ZTWhq4d zOgfZOM(j`c)sBYc?vnxsofMFpThD-#lS-SNmIr>9a4}6>q+T-yG!ygTxQ6BNClkm3 zsPzoLu0Z_kT4`TtPiX31!=N$@PA-}L&>sNfiq)d{1qlu&S%@yp053lnH3uY>Iz##^ zUr95l{=Jn$_@(Xo=&YtlP2jgoG62dr;?C5>BskUv^mTB0cE5D!(0{-}&h~c=Zx~Ho zOPiL@6BtPW5R+QPi?jvCAujdRF`x|)Y6F%{hIXSV%3ma!>+*Ijq_GrodnCUqP~iv~HCk$r&N_Q3C`b|I&#$h{8jV8U0TixPZja^Ljwd?U)FPM5 zMTKy}pCPQSnWOWuTruQ_V@0RC6ndiAAT%+{)a9R7eONxn=d#6(%kGTKw0Q&$8je9| zbkuk8)z$KGKKmh^Lz``n;MrodEWO?+@J!}M)YdS3^gl_G_!jxyR)(oYBNE!P6eu(l z0F!Q&6MwX2uOBk07*qo IM6N<$f*6?is{jB1 diff --git a/packages/synapse-constants/constants/assets/icons/usdb.svg b/packages/synapse-constants/constants/assets/icons/usdb.svg deleted file mode 100644 index 397aae6629..0000000000 --- a/packages/synapse-constants/constants/assets/icons/usdb.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/synapse-constants/constants/assets/icons/usdc.svg b/packages/synapse-constants/constants/assets/icons/usdc.svg deleted file mode 100644 index 0cf54945bd..0000000000 --- a/packages/synapse-constants/constants/assets/icons/usdc.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/usdt.svg b/packages/synapse-constants/constants/assets/icons/usdt.svg deleted file mode 100644 index 1ea97592e0..0000000000 --- a/packages/synapse-constants/constants/assets/icons/usdt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/vsta.svg b/packages/synapse-constants/constants/assets/icons/vsta.svg deleted file mode 100644 index 5b2b1ac3c2..0000000000 --- a/packages/synapse-constants/constants/assets/icons/vsta.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/wbtc.svg b/packages/synapse-constants/constants/assets/icons/wbtc.svg deleted file mode 100644 index 7f9d25c59b..0000000000 --- a/packages/synapse-constants/constants/assets/icons/wbtc.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/weth.svg b/packages/synapse-constants/constants/assets/icons/weth.svg deleted file mode 100644 index 0dec30bb5f..0000000000 --- a/packages/synapse-constants/constants/assets/icons/weth.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/index.ts b/packages/synapse-constants/constants/assets/index.ts deleted file mode 100644 index f32ebc2c18..0000000000 --- a/packages/synapse-constants/constants/assets/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './chains' -export * from './explorer' -export * from './icons' diff --git a/packages/synapse-constants/constants/tokens/bridgeMap.ts b/packages/synapse-constants/constants/tokens/bridgeMap.ts deleted file mode 100644 index 70f9755d4a..0000000000 --- a/packages/synapse-constants/constants/tokens/bridgeMap.ts +++ /dev/null @@ -1,1999 +0,0 @@ -export const BRIDGE_MAP = { - '1': { - '0x0642026E7f0B6cCaC5925b4E7Fa61384250e1701': { - decimals: 18, - symbol: 'H2O', - origin: ['H2O'], - destination: ['H2O'], - swappable: [], - }, - '0x0ab87046fBb341D058F17CBC4c1133F25a20a52f': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0x0f2D719407FdBeFF09D87557AbB7232601FD9F29': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0x12f79f8c1A6e47a9b5F0796FDb008Bdc182fa19e': { - decimals: 18, - symbol: 'JEWEL', - origin: ['JEWEL'], - destination: ['JEWEL'], - swappable: [], - }, - '0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F': { - decimals: 18, - symbol: 'nUSD', - origin: [ - 'CCTP.USDC', - 'DAI', - 'RFQ.USDC', - 'USDC', - 'USDT', - 'nUSD', - 'synFRAX', - ], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0', - '0x6B175474E89094C44Da98b954EedeAC495271d0F', - '0x6c3ea9036406852006290770BEdFcAbA0e23A0e8', - '0x853d955aCEf822Db058eb8505911ED77F175b99e', - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - '0xdAC17F958D2ee523a2206206994597C13D831ec7', - '0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E', - ], - }, - '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599': { - decimals: 8, - symbol: 'WBTC', - origin: ['WBTC'], - destination: ['WBTC'], - swappable: [], - }, - '0x514910771AF9Ca656af840dff83E8264EcF986CA': { - decimals: 18, - symbol: 'LINK', - origin: ['LINK'], - destination: ['LINK'], - swappable: [], - }, - '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0': { - decimals: 18, - symbol: 'LUSD', - origin: [ - 'CCTP.USDC', - 'DAI', - 'RFQ.USDC', - 'USDC', - 'USDT', - 'nUSD', - 'synFRAX', - ], - destination: ['CCTP.USDC'], - swappable: [ - '0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F', - '0x6B175474E89094C44Da98b954EedeAC495271d0F', - '0x6c3ea9036406852006290770BEdFcAbA0e23A0e8', - '0x853d955aCEf822Db058eb8505911ED77F175b99e', - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - '0xdAC17F958D2ee523a2206206994597C13D831ec7', - '0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E', - ], - }, - '0x6982508145454Ce325dDbE47a25d4ec3d2311933': { - decimals: 18, - symbol: 'PEPE', - origin: ['PEPE'], - destination: ['PEPE'], - swappable: [], - }, - '0x6B175474E89094C44Da98b954EedeAC495271d0F': { - decimals: 18, - symbol: 'DAI', - origin: [ - 'CCTP.USDC', - 'DAI', - 'RFQ.USDC', - 'USDC', - 'USDT', - 'nUSD', - 'synFRAX', - ], - destination: ['CCTP.USDC', 'DAI', 'nUSD'], - swappable: [ - '0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F', - '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0', - '0x6c3ea9036406852006290770BEdFcAbA0e23A0e8', - '0x853d955aCEf822Db058eb8505911ED77F175b99e', - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - '0xdAC17F958D2ee523a2206206994597C13D831ec7', - '0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E', - ], - }, - '0x6c3ea9036406852006290770BEdFcAbA0e23A0e8': { - decimals: 6, - symbol: 'PYUSD', - origin: [ - 'CCTP.USDC', - 'DAI', - 'RFQ.USDC', - 'USDC', - 'USDT', - 'nUSD', - 'synFRAX', - ], - destination: ['CCTP.USDC'], - swappable: [ - '0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F', - '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0', - '0x6B175474E89094C44Da98b954EedeAC495271d0F', - '0x853d955aCEf822Db058eb8505911ED77F175b99e', - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - '0xdAC17F958D2ee523a2206206994597C13D831ec7', - '0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E', - ], - }, - '0x71Ab77b7dbB4fa7e017BC15090b2163221420282': { - decimals: 18, - symbol: 'HIGH', - origin: ['HIGH'], - destination: ['HIGH'], - swappable: [], - }, - '0x73968b9a57c6E53d41345FD57a6E6ae27d6CDB2F': { - decimals: 18, - symbol: 'SDT', - origin: ['SDT'], - destination: ['SDT'], - swappable: [], - }, - '0x853d955aCEf822Db058eb8505911ED77F175b99e': { - decimals: 18, - symbol: 'FRAX', - origin: [ - 'CCTP.USDC', - 'DAI', - 'RFQ.USDC', - 'USDC', - 'USDT', - 'nUSD', - 'synFRAX', - ], - destination: ['CCTP.USDC', 'synFRAX'], - swappable: [ - '0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F', - '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0', - '0x6B175474E89094C44Da98b954EedeAC495271d0F', - '0x6c3ea9036406852006290770BEdFcAbA0e23A0e8', - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - '0xdAC17F958D2ee523a2206206994597C13D831ec7', - '0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E', - ], - }, - '0x98585dFc8d9e7D48F0b1aE47ce33332CF4237D96': { - decimals: 18, - symbol: 'NEWO', - origin: ['NEWO'], - destination: ['NEWO'], - swappable: [], - }, - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48': { - decimals: 6, - symbol: 'USDC', - origin: [ - 'CCTP.USDC', - 'DAI', - 'RFQ.USDC', - 'USDC', - 'USDT', - 'nUSD', - 'synFRAX', - ], - destination: ['CCTP.USDC', 'USDC', 'nUSD', 'RFQ.USDC'], - swappable: [ - '0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F', - '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0', - '0x6B175474E89094C44Da98b954EedeAC495271d0F', - '0x6c3ea9036406852006290770BEdFcAbA0e23A0e8', - '0x853d955aCEf822Db058eb8505911ED77F175b99e', - '0xdAC17F958D2ee523a2206206994597C13D831ec7', - '0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E', - ], - }, - '0xA8d7F5e7C78ed0Fa097Cc5Ec66C1DC3104c9bbeb': { - decimals: 18, - symbol: 'VSTA', - origin: ['VSTA'], - destination: ['VSTA'], - swappable: [], - }, - '0xAdF7C35560035944e805D98fF17d58CDe2449389': { - decimals: 18, - symbol: 'SPEC', - origin: ['SPEC'], - destination: ['SPEC'], - swappable: [], - }, - '0xBAac2B4491727D78D2b78815144570b9f2Fe8899': { - decimals: 18, - symbol: 'DOG', - origin: ['DOG'], - destination: ['DOG'], - swappable: [], - }, - '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2': { - decimals: 18, - symbol: 'WETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH'], - swappable: [], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'ETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH', 'RFQ.ETH'], - swappable: [], - }, - '0xb753428af26E81097e7fD17f40c88aaA3E04902c': { - decimals: 18, - symbol: 'SFI', - origin: ['SFI'], - destination: ['SFI'], - swappable: [], - }, - '0xdAC17F958D2ee523a2206206994597C13D831ec7': { - decimals: 6, - symbol: 'USDT', - origin: [ - 'CCTP.USDC', - 'DAI', - 'RFQ.USDC', - 'USDC', - 'USDT', - 'nUSD', - 'synFRAX', - ], - destination: ['CCTP.USDC', 'USDT', 'nUSD'], - swappable: [ - '0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F', - '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0', - '0x6B175474E89094C44Da98b954EedeAC495271d0F', - '0x6c3ea9036406852006290770BEdFcAbA0e23A0e8', - '0x853d955aCEf822Db058eb8505911ED77F175b99e', - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - '0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E', - ], - }, - '0xf0655DcEE37E5C0b70Fffd70D85f88F8eDf0AfF6': { - decimals: 18, - symbol: 'UNIDX', - origin: ['UNIDX'], - destination: ['UNIDX'], - swappable: [], - }, - '0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E': { - decimals: 18, - symbol: 'crvUSD', - origin: [ - 'CCTP.USDC', - 'DAI', - 'RFQ.USDC', - 'USDC', - 'USDT', - 'nUSD', - 'synFRAX', - ], - destination: ['CCTP.USDC'], - swappable: [ - '0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F', - '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0', - '0x6B175474E89094C44Da98b954EedeAC495271d0F', - '0x6c3ea9036406852006290770BEdFcAbA0e23A0e8', - '0x853d955aCEf822Db058eb8505911ED77F175b99e', - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - '0xdAC17F958D2ee523a2206206994597C13D831ec7', - ], - }, - }, - '10': { - '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85': { - decimals: 6, - symbol: 'USDC', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'RFQ.USDC'], - swappable: [ - '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00', - '0x7F5c764cBc14f9669B88837ca1490cCa17c31607', - '0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9', - '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - ], - }, - '0x0b5740c6b4a97f90eF2F0220651Cca420B868FfB': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0x121ab82b49B2BC4c7901CA46B8277962b4350204': { - decimals: 18, - symbol: 'WETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH'], - swappable: [ - '0x809DC529f07651bD43A172e8dB6f4a7a0d771036', - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - ], - }, - '0x28b42698Caf46B4B012CF38b6C75867E0762186D': { - decimals: 18, - symbol: 'UNIDX', - origin: ['UNIDX'], - destination: ['UNIDX'], - swappable: [], - }, - '0x5A5fFf6F753d7C11A56A52FE47a177a87e431655': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00': { - decimals: 18, - symbol: 'nUSD', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', - '0x7F5c764cBc14f9669B88837ca1490cCa17c31607', - '0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9', - '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - ], - }, - '0x7F5c764cBc14f9669B88837ca1490cCa17c31607': { - decimals: 6, - symbol: 'USDC.e', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', - '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00', - '0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9', - '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - ], - }, - '0x809DC529f07651bD43A172e8dB6f4a7a0d771036': { - decimals: 18, - symbol: 'nETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH'], - swappable: [ - '0x121ab82b49B2BC4c7901CA46B8277962b4350204', - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - ], - }, - '0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9': { - decimals: 18, - symbol: 'sUSD', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', - '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00', - '0x7F5c764cBc14f9669B88837ca1490cCa17c31607', - '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - ], - }, - '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58': { - decimals: 6, - symbol: 'USDT', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', - '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00', - '0x7F5c764cBc14f9669B88837ca1490cCa17c31607', - '0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - ], - }, - '0xD9eAA386cCD65F30b77FF175F6b52115FE454fD6': { - decimals: 18, - symbol: 'PLS', - origin: ['PLS'], - destination: ['PLS'], - swappable: [], - }, - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1': { - decimals: 18, - symbol: 'DAI', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', - '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00', - '0x7F5c764cBc14f9669B88837ca1490cCa17c31607', - '0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9', - '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', - ], - }, - '0xE3c82A836Ec85311a433fBd9486EfAF4b1AFbF48': { - decimals: 18, - symbol: 'H2O', - origin: ['H2O'], - destination: ['H2O'], - swappable: [], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'ETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH', 'RFQ.ETH'], - swappable: [ - '0x121ab82b49B2BC4c7901CA46B8277962b4350204', - '0x809DC529f07651bD43A172e8dB6f4a7a0d771036', - ], - }, - '0xd52f94DF742a6F4B4C8b033369fE13A41782Bf44': { - decimals: 18, - symbol: 'L2DAO', - origin: ['L2DAO'], - destination: ['L2DAO'], - swappable: [], - }, - }, - '25': { - '0x396c9c192dd323995346632581BEF92a31AC623b': { - decimals: 18, - symbol: 'nUSD', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: ['0xc21223249CA28397B4B6541dfFaEcC539BfF0c59'], - }, - '0xFD0F80899983b8D46152aa1717D76cba71a31616': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0xbB0A63A6CA2071c6C4bcAC11a1A317b20E3E999C': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0xc21223249CA28397B4B6541dfFaEcC539BfF0c59': { - decimals: 6, - symbol: 'USDC', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: ['0x396c9c192dd323995346632581BEF92a31AC623b'], - }, - }, - '56': { - '0x03eFca7CEb108734D3777684F3C0A0d8ad652f79': { - decimals: 18, - symbol: 'H2O', - origin: ['H2O'], - destination: ['H2O'], - swappable: [], - }, - '0x0FE9778c005a5A6115cBE12b0568a2d50b765A51': { - decimals: 18, - symbol: 'NFD', - origin: ['NFD'], - destination: ['NFD'], - swappable: [], - }, - '0x130025eE738A66E691E6A7a62381CB33c6d9Ae83': { - decimals: 18, - symbol: 'JUMP', - origin: ['JUMP'], - destination: ['JUMP'], - swappable: [], - }, - '0x23b891e5C62E0955ae2bD185990103928Ab817b3': { - decimals: 18, - symbol: 'nUSD', - origin: ['BUSD', 'nUSD'], - destination: ['nUSD'], - swappable: [ - '0x55d398326f99059fF775485246999027B3197955', - '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', - '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56', - ], - }, - '0x55d398326f99059fF775485246999027B3197955': { - decimals: 18, - symbol: 'USDT', - origin: ['BUSD', 'nUSD'], - destination: ['nUSD'], - swappable: [ - '0x23b891e5C62E0955ae2bD185990103928Ab817b3', - '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', - '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56', - ], - }, - '0x5f4Bde007Dc06b867f86EBFE4802e34A1fFEEd63': { - decimals: 18, - symbol: 'HIGH', - origin: ['HIGH'], - destination: ['HIGH'], - swappable: [], - }, - '0x88918495892BAF4536611E38E75D771Dc6Ec0863': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d': { - decimals: 18, - symbol: 'USDC', - origin: ['BUSD', 'nUSD'], - destination: ['nUSD'], - swappable: [ - '0x23b891e5C62E0955ae2bD185990103928Ab817b3', - '0x55d398326f99059fF775485246999027B3197955', - '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56', - ], - }, - '0xa4080f1778e69467E905B8d6F72f6e441f9e9484': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0xaA88C603d142C371eA0eAC8756123c5805EdeE03': { - decimals: 18, - symbol: 'DOG', - origin: ['DOG'], - destination: ['DOG'], - swappable: [], - }, - '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56': { - decimals: 18, - symbol: 'BUSD', - origin: ['BUSD', 'nUSD'], - destination: ['BUSD', 'nUSD'], - swappable: [ - '0x23b891e5C62E0955ae2bD185990103928Ab817b3', - '0x55d398326f99059fF775485246999027B3197955', - '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', - ], - }, - }, - '137': { - '0x0A5926027d407222F8fe20f24cB16e103f617046': { - decimals: 18, - symbol: 'NFD', - origin: ['NFD'], - destination: ['NFD'], - swappable: [], - }, - '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270': { - decimals: 18, - symbol: 'WMATIC', - origin: ['MATIC'], - destination: ['MATIC'], - swappable: [], - }, - '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174': { - decimals: 6, - symbol: 'USDC.e', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', - '0x45c32fA6DF82ead1e2EF74d17b76547EDdFaFF89', - '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', - '0xB6c473756050dE474286bED418B77Aeac39B02aF', - '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', - ], - }, - '0x32ba7cF7d681357529013de6a2CDF93933C0dF3f': { - decimals: 18, - symbol: 'H2O', - origin: ['H2O'], - destination: ['H2O'], - swappable: [], - }, - '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359': { - decimals: 6, - symbol: 'USDC', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', - '0x45c32fA6DF82ead1e2EF74d17b76547EDdFaFF89', - '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', - '0xB6c473756050dE474286bED418B77Aeac39B02aF', - '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', - ], - }, - '0x45c32fA6DF82ead1e2EF74d17b76547EDdFaFF89': { - decimals: 18, - symbol: 'FRAX', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', - '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', - '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', - '0xB6c473756050dE474286bED418B77Aeac39B02aF', - '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', - ], - }, - '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063': { - decimals: 18, - symbol: 'DAI', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', - '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', - '0x45c32fA6DF82ead1e2EF74d17b76547EDdFaFF89', - '0xB6c473756050dE474286bED418B77Aeac39B02aF', - '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', - ], - }, - '0xB6c473756050dE474286bED418B77Aeac39B02aF': { - decimals: 18, - symbol: 'nUSD', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', - '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', - '0x45c32fA6DF82ead1e2EF74d17b76547EDdFaFF89', - '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', - '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', - ], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'MATIC', - origin: ['MATIC'], - destination: ['MATIC'], - swappable: [], - }, - '0xc2132D05D31c914a87C6611C10748AEb04B58e8F': { - decimals: 6, - symbol: 'USDT', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', - '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', - '0x45c32fA6DF82ead1e2EF74d17b76547EDdFaFF89', - '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', - '0xB6c473756050dE474286bED418B77Aeac39B02aF', - ], - }, - '0xd8cA34fd379d9ca3C6Ee3b3905678320F5b45195': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0xeEe3371B89FC43Ea970E908536Fcddd975135D8a': { - decimals: 18, - symbol: 'DOG', - origin: ['DOG'], - destination: ['DOG'], - swappable: [], - }, - '0xf8F9efC0db77d8881500bb06FF5D6ABc3070E695': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - }, - '250': { - '0x0483a76D80D0aFEC6bd2afd12C1AD865b9DF1471': { - decimals: 18, - symbol: 'UNIDX', - origin: ['UNIDX'], - destination: ['UNIDX'], - swappable: [], - }, - '0x1852F70512298d56e9c8FDd905e02581E04ddb2a': { - decimals: 18, - symbol: 'synFRAX', - origin: ['synFRAX'], - destination: ['synFRAX'], - swappable: [], - }, - '0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83': { - decimals: 18, - symbol: 'WFTM', - origin: ['FTM'], - destination: ['FTM'], - swappable: [], - }, - '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00': { - decimals: 18, - symbol: 'nETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [], - }, - '0x78DE9326792ce1d6eCA0c978753c6953Cdeedd73': { - decimals: 18, - symbol: 'JUMP', - origin: ['JUMP'], - destination: ['JUMP'], - swappable: [], - }, - '0x91fa20244Fb509e8289CA630E5db3E9166233FDc': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0xE3c82A836Ec85311a433fBd9486EfAF4b1AFbF48': { - decimals: 18, - symbol: 'SDT', - origin: ['SDT'], - destination: ['SDT'], - swappable: [], - }, - '0xE55e19Fb4F2D85af758950957714292DAC1e25B2': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0xED2a7edd7413021d440b09D654f3b87712abAB66': { - decimals: 18, - symbol: 'nUSD', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'FTM', - origin: ['FTM'], - destination: ['FTM'], - swappable: [], - }, - }, - '288': { - '0x5DE1677344D3Cb0D7D465c10b72A8f60699C062d': { - decimals: 6, - symbol: 'USDT', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x66a2A913e447d6b4BF33EFbec43aAeF87890FBbc', - '0x6B4712AE9797C199edd44F897cA09BC57628a1CF', - '0xf74195Bb8a5cf652411867c5C2C5b8C2a402be35', - ], - }, - '0x66a2A913e447d6b4BF33EFbec43aAeF87890FBbc': { - decimals: 6, - symbol: 'USDC', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x5DE1677344D3Cb0D7D465c10b72A8f60699C062d', - '0x6B4712AE9797C199edd44F897cA09BC57628a1CF', - '0xf74195Bb8a5cf652411867c5C2C5b8C2a402be35', - ], - }, - '0x6B4712AE9797C199edd44F897cA09BC57628a1CF': { - decimals: 18, - symbol: 'nUSD', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x5DE1677344D3Cb0D7D465c10b72A8f60699C062d', - '0x66a2A913e447d6b4BF33EFbec43aAeF87890FBbc', - '0xf74195Bb8a5cf652411867c5C2C5b8C2a402be35', - ], - }, - '0x96419929d7949D6A801A6909c145C8EEf6A40431': { - decimals: 18, - symbol: 'nETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [ - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - '0xd203De32170130082896b4111eDF825a4774c18E', - ], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'ETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [ - '0x96419929d7949D6A801A6909c145C8EEf6A40431', - '0xd203De32170130082896b4111eDF825a4774c18E', - ], - }, - '0xb554A55358fF0382Fb21F0a478C3546d1106Be8c': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0xd203De32170130082896b4111eDF825a4774c18E': { - decimals: 18, - symbol: 'WETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [ - '0x96419929d7949D6A801A6909c145C8EEf6A40431', - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - ], - }, - '0xd22C0a4Af486C7FA08e282E9eB5f30F9AaA62C95': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0xf74195Bb8a5cf652411867c5C2C5b8C2a402be35': { - decimals: 18, - symbol: 'DAI', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x5DE1677344D3Cb0D7D465c10b72A8f60699C062d', - '0x66a2A913e447d6b4BF33EFbec43aAeF87890FBbc', - '0x6B4712AE9797C199edd44F897cA09BC57628a1CF', - ], - }, - }, - '1088': { - '0x17C09cfC96C865CF546d73365Cedb6dC66986963': { - decimals: 18, - symbol: 'JEWEL', - origin: ['JEWEL'], - destination: ['JEWEL'], - swappable: [], - }, - '0x420000000000000000000000000000000000000A': { - decimals: 18, - symbol: 'WETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: ['0x931B8f17764362A3325D30681009f0eDd6211231'], - }, - '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0x931B8f17764362A3325D30681009f0eDd6211231': { - decimals: 18, - symbol: 'nETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: ['0x420000000000000000000000000000000000000A'], - }, - '0x961318Fc85475E125B99Cc9215f62679aE5200aB': { - decimals: 18, - symbol: 'nUSD', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: ['0xEA32A96608495e54156Ae48931A7c20f0dcc1a21'], - }, - '0xE3c82A836Ec85311a433fBd9486EfAF4b1AFbF48': { - decimals: 18, - symbol: 'JUMP', - origin: ['JUMP'], - destination: ['JUMP'], - swappable: [], - }, - '0xEA32A96608495e54156Ae48931A7c20f0dcc1a21': { - decimals: 6, - symbol: 'm.USDC', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: ['0x961318Fc85475E125B99Cc9215f62679aE5200aB'], - }, - '0xFB21B70922B9f6e3C6274BcD6CB1aa8A0fe20B80': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - }, - '1284': { - '0x0DB6729C03C85B0708166cA92801BcB5CAc781fC': { - decimals: 18, - symbol: 'veSOLAR', - origin: ['veSOLAR'], - destination: ['veSOLAR'], - swappable: [], - }, - '0x1d4C2a246311bB9f827F4C768e277FF5787B7D7E': { - decimals: 18, - symbol: 'MOVR', - origin: ['MOVR'], - destination: ['MOVR'], - swappable: [], - }, - '0x3192Ae73315c3634Ffa217f71CF6CBc30FeE349A': { - decimals: 18, - symbol: 'WETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [], - }, - '0xA1f8890E39b4d8E33efe296D698fe42Fb5e59cC3': { - decimals: 18, - symbol: 'AVAX', - origin: ['AVAX'], - destination: ['AVAX'], - swappable: [], - }, - '0xA46aDF6D5881ca0b8596EDadF8f058F8c16d8B68': { - decimals: 18, - symbol: 'H2O', - origin: ['H2O'], - destination: ['H2O'], - swappable: [], - }, - '0xD2666441443DAa61492FFe0F37717578714a4521': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0xDd47A348AB60c61Ad6B60cA8C31ea5e00eBfAB4F': { - decimals: 18, - symbol: 'synFRAX', - origin: ['synFRAX'], - destination: ['synFRAX'], - swappable: [], - }, - '0xF44938b0125A6662f9536281aD2CD6c499F22004': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - }, - '1285': { - '0x3bF21Ce864e58731B6f28D68d5928BcBEb0Ad172': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0x76906411D07815491A5E577022757aD941fb5066': { - decimals: 18, - symbol: 'veSOLAR', - origin: ['veSOLAR'], - destination: ['veSOLAR'], - swappable: [], - }, - '0x98878B06940aE243284CA214f92Bb71a2b032B8A': { - decimals: 18, - symbol: 'WMOVR', - origin: ['MOVR'], - destination: ['MOVR'], - swappable: [], - }, - '0x9c0a820bb01e2807aCcd1c682d359e92DDd41403': { - decimals: 18, - symbol: 'H2O', - origin: ['H2O'], - destination: ['H2O'], - swappable: [], - }, - '0xE96AC70907ffF3Efee79f502C985A7A21Bce407d': { - decimals: 18, - symbol: 'synFRAX', - origin: ['synFRAX'], - destination: ['synFRAX'], - swappable: [], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'MOVR', - origin: ['MOVR'], - destination: ['MOVR'], - swappable: [], - }, - '0xd80d8688b02B3FD3afb81cDb124F188BB5aD0445': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - }, - '2000': { - '0x10D70831f9C3c11c5fe683b2f1Be334503880DB6': { - decimals: 18, - symbol: 'FRAX', - origin: ['synFRAX'], - destination: ['synFRAX'], - swappable: [], - }, - '0x1555C68Be3b22cdcCa934Ae88Cb929Db40aB311d': { - decimals: 18, - symbol: 'BUSD', - origin: ['BUSD'], - destination: ['BUSD'], - swappable: [], - }, - '0x7f8e71DD5A7e445725F0EF94c7F01806299e877A': { - decimals: 6, - symbol: 'USDT', - origin: ['USDT'], - destination: ['USDT'], - swappable: [], - }, - '0x85C2D3bEBffD83025910985389aB8aD655aBC946': { - decimals: 6, - symbol: 'USDC', - origin: ['USDC'], - destination: ['USDC'], - swappable: [], - }, - '0x868055ADFA27D331d5b69b1BF3469aDAAc3ba7f2': { - decimals: 18, - symbol: 'NFD', - origin: ['NFD'], - destination: ['NFD'], - swappable: [], - }, - '0x9F4614E4Ea4A0D7c4B1F946057eC030beE416cbB': { - decimals: 18, - symbol: 'WETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [], - }, - '0xB3306f03595490e5cC3a1b1704a5a158D3436ffC': { - decimals: 18, - symbol: 'DAI', - origin: ['DAI'], - destination: ['DAI'], - swappable: [], - }, - '0xD0c6179c43C00221915f1a61f8eC06A5Aa32F9EC': { - decimals: 8, - symbol: 'WBTC', - origin: ['WBTC'], - destination: ['WBTC'], - swappable: [], - }, - '0xDfA53EeBA61D69E1D2b56b36d78449368F0265c1': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - }, - '7700': { - '0x09fEC30669d63A13c666d2129230dD5588E2e240': { - decimals: 18, - symbol: 'nETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [], - }, - '0x4e71A2E537B7f9D9413D3991D37958c0b5e1e503': { - decimals: 18, - symbol: 'NOTE', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x80b5a32E4F032B2a058b4F29EC95EEfEEB87aDcd', - '0xD8836aF2e565D3Befce7D906Af63ee45a57E8f80', - '0xd567B3d7B8FE3C79a1AD8dA978812cfC4Fa05e75', - ], - }, - '0x555982d2E211745b96736665e19D9308B615F78e': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0x80b5a32E4F032B2a058b4F29EC95EEfEEB87aDcd': { - decimals: 6, - symbol: 'USDC', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x4e71A2E537B7f9D9413D3991D37958c0b5e1e503', - '0xD8836aF2e565D3Befce7D906Af63ee45a57E8f80', - '0xd567B3d7B8FE3C79a1AD8dA978812cfC4Fa05e75', - ], - }, - '0xD8836aF2e565D3Befce7D906Af63ee45a57E8f80': { - decimals: 18, - symbol: 'nUSD', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x4e71A2E537B7f9D9413D3991D37958c0b5e1e503', - '0x80b5a32E4F032B2a058b4F29EC95EEfEEB87aDcd', - '0xd567B3d7B8FE3C79a1AD8dA978812cfC4Fa05e75', - ], - }, - '0xd567B3d7B8FE3C79a1AD8dA978812cfC4Fa05e75': { - decimals: 6, - symbol: 'USDT', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x4e71A2E537B7f9D9413D3991D37958c0b5e1e503', - '0x80b5a32E4F032B2a058b4F29EC95EEfEEB87aDcd', - '0xD8836aF2e565D3Befce7D906Af63ee45a57E8f80', - ], - }, - }, - '8217': { - '0x078dB7827a5531359f6CB63f62CFA20183c4F10c': { - decimals: 18, - symbol: 'DAI', - origin: ['DAI'], - destination: ['DAI'], - swappable: [], - }, - '0x30C103f8f5A3A732DFe2dCE1Cc9446f545527b43': { - decimals: 18, - symbol: 'JEWEL', - origin: ['JEWEL'], - destination: ['JEWEL'], - swappable: [], - }, - '0x5819b6af194A78511c79C85Ea68D2377a7e9335f': { - decimals: 18, - symbol: 'WKLAY', - origin: ['KLAY'], - destination: ['KLAY'], - swappable: [], - }, - '0x6270B58BE569a7c0b8f47594F191631Ae5b2C86C': { - decimals: 6, - symbol: 'USDC', - origin: ['USDC'], - destination: ['USDC'], - swappable: [], - }, - '0xCD6f29dC9Ca217d0973d3D21bF58eDd3CA871a86': { - decimals: 18, - symbol: 'WETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [], - }, - '0xCd8fE44A29Db9159dB36f96570d7A4d91986f528': { - decimals: 18, - symbol: 'AVAX', - origin: ['AVAX'], - destination: ['AVAX'], - swappable: [], - }, - '0xDCbacF3f7a069922E677912998c8d57423C37dfA': { - decimals: 8, - symbol: 'WBTC', - origin: ['WBTC'], - destination: ['WBTC'], - swappable: [], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'KLAY', - origin: ['KLAY'], - destination: ['KLAY'], - swappable: [], - }, - '0xd6dAb4CfF47dF175349e6e7eE2BF7c40Bb8C05A3': { - decimals: 6, - symbol: 'USDT', - origin: ['USDT'], - destination: ['USDT'], - swappable: [], - }, - '0xe82f87ba4E97b2796aA0Fa4eFB06e8f0d2EB4FE1': { - decimals: 8, - symbol: 'BTC.b', - origin: ['BTCB'], - destination: ['BTCB'], - swappable: [], - }, - '0xfbEd1AbB3aD0f8C467068De9fDE905887e8C9118': { - decimals: 18, - symbol: 'LINK', - origin: ['LINK'], - destination: ['LINK'], - swappable: [], - }, - }, - '8453': { - '0x417Ac0e078398C154EdFadD9Ef675d30Be60Af93': { - decimals: 18, - symbol: 'crvUSD', - origin: ['CCTP.USDC', 'RFQ.USDC'], - destination: ['CCTP.USDC'], - swappable: [ - '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb', - '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', - '0xEB466342C4d449BC9f53A865D5Cb90586f405215', - '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA', - ], - }, - '0x4200000000000000000000000000000000000006': { - decimals: 18, - symbol: 'WETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH'], - swappable: [ - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - '0xb554A55358fF0382Fb21F0a478C3546d1106Be8c', - ], - }, - '0x432036208d2717394d2614d6697c46DF3Ed69540': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb': { - decimals: 18, - symbol: 'DAI', - origin: ['CCTP.USDC', 'RFQ.USDC'], - destination: ['CCTP.USDC'], - swappable: [ - '0x417Ac0e078398C154EdFadD9Ef675d30Be60Af93', - '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', - '0xEB466342C4d449BC9f53A865D5Cb90586f405215', - '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA', - ], - }, - '0x6B4712AE9797C199edd44F897cA09BC57628a1CF': { - decimals: 18, - symbol: 'UNIDX', - origin: ['UNIDX'], - destination: ['UNIDX'], - swappable: [], - }, - '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913': { - decimals: 6, - symbol: 'USDC', - origin: ['CCTP.USDC', 'RFQ.USDC'], - destination: ['CCTP.USDC', 'RFQ.USDC'], - swappable: [ - '0x417Ac0e078398C154EdFadD9Ef675d30Be60Af93', - '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb', - '0xEB466342C4d449BC9f53A865D5Cb90586f405215', - '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA', - ], - }, - '0x96419929d7949D6A801A6909c145C8EEf6A40431': { - decimals: 18, - symbol: 'SPEC', - origin: ['SPEC'], - destination: ['SPEC'], - swappable: [], - }, - '0xEB466342C4d449BC9f53A865D5Cb90586f405215': { - decimals: 6, - symbol: 'axlUSDC', - origin: ['CCTP.USDC', 'RFQ.USDC'], - destination: ['CCTP.USDC'], - swappable: [ - '0x417Ac0e078398C154EdFadD9Ef675d30Be60Af93', - '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb', - '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', - '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA', - ], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'ETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH', 'RFQ.ETH'], - swappable: [ - '0x4200000000000000000000000000000000000006', - '0xb554A55358fF0382Fb21F0a478C3546d1106Be8c', - ], - }, - '0xb554A55358fF0382Fb21F0a478C3546d1106Be8c': { - decimals: 18, - symbol: 'nETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH'], - swappable: [ - '0x4200000000000000000000000000000000000006', - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - ], - }, - '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA': { - decimals: 6, - symbol: 'USDbC', - origin: ['CCTP.USDC', 'RFQ.USDC'], - destination: ['CCTP.USDC'], - swappable: [ - '0x417Ac0e078398C154EdFadD9Ef675d30Be60Af93', - '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb', - '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', - '0xEB466342C4d449BC9f53A865D5Cb90586f405215', - ], - }, - }, - '42161': { - '0x080F6AEd32Fc474DD5717105Dba5ea57268F46eb': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0x0877154a755B24D499B8e2bD7ecD54d3c92BA433': { - decimals: 18, - symbol: 'NEWO', - origin: ['NEWO'], - destination: ['NEWO'], - swappable: [], - }, - '0x087d18A77465c34CDFd3a081a2504b7E86CE4EF8': { - decimals: 18, - symbol: 'SDT', - origin: ['SDT'], - destination: ['SDT'], - swappable: [], - }, - '0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F': { - decimals: 18, - symbol: 'FRAX', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x2913E812Cf0dcCA30FB28E6Cac3d2DCFF4497688', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', - '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', - '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', - ], - }, - '0x2913E812Cf0dcCA30FB28E6Cac3d2DCFF4497688': { - decimals: 18, - symbol: 'nUSD', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', - '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', - '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', - ], - }, - '0x2CaB3abfC1670D1a452dF502e216a66883cDf079': { - decimals: 18, - symbol: 'L2DAO', - origin: ['L2DAO'], - destination: ['L2DAO'], - swappable: [], - }, - '0x3ea9B0ab55F34Fb188824Ee288CeaEfC63cf908e': { - decimals: 18, - symbol: 'nETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH'], - swappable: [ - '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - ], - }, - '0x51318B7D00db7ACc4026C88c3952B66278B6A67F': { - decimals: 18, - symbol: 'PLS', - origin: ['PLS'], - destination: ['PLS'], - swappable: [], - }, - '0x5429706887FCb58a595677B73E9B0441C25d993D': { - decimals: 18, - symbol: 'UNIDX', - origin: ['UNIDX'], - destination: ['UNIDX'], - swappable: [], - }, - '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1': { - decimals: 18, - symbol: 'WETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH'], - swappable: [ - '0x3ea9B0ab55F34Fb188824Ee288CeaEfC63cf908e', - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - ], - }, - '0x8D9bA570D6cb60C7e3e0F31343Efe75AB8E65FB1': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0xA54B8e178A49F8e5405A4d44Bb31F496e5564A05': { - decimals: 18, - symbol: 'PEPE', - origin: ['PEPE'], - destination: ['PEPE'], - swappable: [], - }, - '0xD1c6f989e9552DB523aBAE2378227fBb059a3976': { - decimals: 18, - symbol: 'H2O', - origin: ['H2O'], - destination: ['H2O'], - swappable: [], - }, - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1': { - decimals: 18, - symbol: 'DAI', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F', - '0x2913E812Cf0dcCA30FB28E6Cac3d2DCFF4497688', - '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', - '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', - '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', - ], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'ETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH', 'RFQ.ETH'], - swappable: [ - '0x3ea9B0ab55F34Fb188824Ee288CeaEfC63cf908e', - '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', - ], - }, - '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8': { - decimals: 6, - symbol: 'USDC.e', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F', - '0x2913E812Cf0dcCA30FB28E6Cac3d2DCFF4497688', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', - '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', - ], - }, - '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9': { - decimals: 6, - symbol: 'USDT', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F', - '0x2913E812Cf0dcCA30FB28E6Cac3d2DCFF4497688', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', - '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', - ], - }, - '0xa684cd057951541187f288294a1e1C2646aA2d24': { - decimals: 18, - symbol: 'VSTA', - origin: ['VSTA'], - destination: ['VSTA'], - swappable: [], - }, - '0xaf88d065e77c8cC2239327C5EDb3A432268e5831': { - decimals: 6, - symbol: 'USDC', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'RFQ.USDC'], - swappable: [ - '0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F', - '0x2913E812Cf0dcCA30FB28E6Cac3d2DCFF4497688', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', - '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', - ], - }, - '0xfc5A1A6EB076a2C7aD06eD22C90d7E710E35ad0a': { - decimals: 18, - symbol: 'GMX', - origin: ['GMX'], - destination: ['GMX'], - swappable: [], - }, - }, - '43114': { - '0x152b9d0FdC40C096757F570A51E494bd4b943E50': { - decimals: 8, - symbol: 'BTC.b', - origin: ['BTCB'], - destination: ['BTCB'], - swappable: [], - }, - '0x19E1ae0eE35c0404f835521146206595d37981ae': { - decimals: 18, - symbol: 'nETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: ['0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB'], - }, - '0x1f1E7c893855525b303f99bDF5c3c05Be09ca251': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0x321E7092a180BB43555132ec53AaA65a5bF84251': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB': { - decimals: 18, - symbol: 'WETH.e', - origin: ['nETH'], - destination: ['nETH'], - swappable: ['0x19E1ae0eE35c0404f835521146206595d37981ae'], - }, - '0x4Bfc90322dD638F81F034517359BD447f8E0235a': { - decimals: 18, - symbol: 'NEWO', - origin: ['NEWO'], - destination: ['NEWO'], - swappable: [], - }, - '0x62edc0692BD897D2295872a9FFCac5425011c661': { - decimals: 18, - symbol: 'GMX', - origin: ['GMX'], - destination: ['GMX'], - swappable: [], - }, - '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7': { - decimals: 6, - symbol: 'USDT', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664', - '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', - '0xCFc37A6AB183dd4aED08C204D1c2773c0b1BDf46', - '0xc7198437980c041c805A1EDcbA50c1Ce5db95118', - '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70', - ], - }, - '0x997Ddaa07d716995DE90577C123Db411584E5E46': { - decimals: 18, - symbol: 'JEWEL', - origin: ['JEWEL'], - destination: ['JEWEL'], - swappable: [], - }, - '0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664': { - decimals: 6, - symbol: 'USDC.e', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7', - '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', - '0xCFc37A6AB183dd4aED08C204D1c2773c0b1BDf46', - '0xc7198437980c041c805A1EDcbA50c1Ce5db95118', - '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70', - ], - }, - '0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7': { - decimals: 18, - symbol: 'WAVAX', - origin: ['AVAX'], - destination: ['AVAX'], - swappable: [], - }, - '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E': { - decimals: 6, - symbol: 'USDC', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7', - '0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664', - '0xCFc37A6AB183dd4aED08C204D1c2773c0b1BDf46', - '0xc7198437980c041c805A1EDcbA50c1Ce5db95118', - '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70', - ], - }, - '0xC6b11a4Fd833d1117E9D312c02865647cd961107': { - decimals: 18, - symbol: 'H2O', - origin: ['H2O'], - destination: ['H2O'], - swappable: [], - }, - '0xCCBf7c451F81752F7d2237F2c18C371E6e089E69': { - decimals: 18, - symbol: 'SDT', - origin: ['SDT'], - destination: ['SDT'], - swappable: [], - }, - '0xCFc37A6AB183dd4aED08C204D1c2773c0b1BDf46': { - decimals: 18, - symbol: 'nUSD', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7', - '0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664', - '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', - '0xc7198437980c041c805A1EDcbA50c1Ce5db95118', - '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70', - ], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'AVAX', - origin: ['AVAX'], - destination: ['AVAX'], - swappable: [], - }, - '0xc2Bf0A1f7D8Da50D608bc96CF701110d4A438312': { - decimals: 18, - symbol: 'SFI', - origin: ['SFI'], - destination: ['SFI'], - swappable: [], - }, - '0xc7198437980c041c805A1EDcbA50c1Ce5db95118': { - decimals: 6, - symbol: 'USDT.e', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7', - '0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664', - '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', - '0xCFc37A6AB183dd4aED08C204D1c2773c0b1BDf46', - '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70', - ], - }, - '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70': { - decimals: 18, - symbol: 'DAI.e', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7', - '0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664', - '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', - '0xCFc37A6AB183dd4aED08C204D1c2773c0b1BDf46', - '0xc7198437980c041c805A1EDcbA50c1Ce5db95118', - ], - }, - '0xf1293574EE43950E7a8c9F1005Ff097A9A713959': { - decimals: 18, - symbol: 'NFD', - origin: ['NFD'], - destination: ['NFD'], - swappable: [], - }, - }, - '53935': { - '0x2Df041186C844F8a2e2b63F16145Bc6Ff7d23E25': { - decimals: 18, - symbol: 'FTM', - origin: ['FTM'], - destination: ['FTM'], - swappable: [], - }, - '0x3AD9DFE640E1A9Cc1D9B0948620820D975c3803a': { - decimals: 18, - symbol: 'USDC', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [], - }, - '0x7516EB8B8Edfa420f540a162335eACF3ea05a247': { - decimals: 8, - symbol: 'BTC.b', - origin: ['BTCB'], - destination: ['BTCB'], - swappable: [], - }, - '0x77f2656d04E158f915bC22f07B779D94c1DC47Ff': { - decimals: 18, - symbol: 'xJEWEL', - origin: ['xJEWEL'], - destination: ['xJEWEL'], - swappable: [], - }, - '0x97855Ba65aa7ed2F65Ed832a776537268158B78a': { - decimals: 18, - symbol: 'KLAY', - origin: ['KLAY'], - destination: ['KLAY'], - swappable: [], - }, - '0xB57B60DeBDB0b8172bb6316a9164bd3C695F133a': { - decimals: 18, - symbol: 'AVAX', - origin: ['AVAX'], - destination: ['AVAX'], - swappable: [], - }, - '0xCCb93dABD71c8Dad03Fc4CE5559dC3D89F67a260': { - decimals: 18, - symbol: 'WJEWEL', - origin: ['JEWEL'], - destination: ['JEWEL'], - swappable: [], - }, - '0xD17a41Cd199edF1093A9Be4404EaDe52Ec19698e': { - decimals: 18, - symbol: 'MATIC', - origin: ['MATIC'], - destination: ['MATIC'], - swappable: [], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'JEWEL', - origin: ['JEWEL'], - destination: ['JEWEL'], - swappable: [], - }, - '0xfBDF0E31808d0aa7b9509AA6aBC9754E48C58852': { - decimals: 18, - symbol: 'ETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [], - }, - }, - '59144': { - '0x176211869cA2b568f2A7D4EE941E073a821EE1ff': { - origin: ['RFQ.USDC'], - destination: ['RFQ.USDC'], - swappable: [], - symbol: 'USDC', - decimals: 6, - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - origin: ['RFQ.ETH'], - destination: ['RFQ.ETH'], - swappable: [], - symbol: 'ETH', - decimals: 18, - }, - }, - '81457': { - '0x3194B0A295D87fDAA54DF852c248F7a6BAF6c6e0': { - decimals: 18, - symbol: 'nUSD', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: ['0x4300000000000000000000000000000000000003'], - }, - '0x4300000000000000000000000000000000000003': { - decimals: 18, - symbol: 'USDB', - origin: ['nUSD', 'RFQ.USDB'], - destination: ['nUSD', 'RFQ.USDB'], - swappable: ['0x3194B0A295D87fDAA54DF852c248F7a6BAF6c6e0'], - }, - '0x4300000000000000000000000000000000000004': { - decimals: 18, - symbol: 'WETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH'], - swappable: [ - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - '0xce971282fAAc9faBcF121944956da7142cccC855', - ], - }, - '0x9592f08387134e218327E6E8423400eb845EdE0E': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'ETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH', 'RFQ.ETH'], - swappable: [ - '0x4300000000000000000000000000000000000004', - '0xce971282fAAc9faBcF121944956da7142cccC855', - ], - }, - '0xce971282fAAc9faBcF121944956da7142cccC855': { - decimals: 18, - symbol: 'nETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH'], - swappable: [ - '0x4300000000000000000000000000000000000004', - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - ], - }, - }, - '534352': { - '0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4': { - origin: ['RFQ.USDC'], - destination: ['RFQ.USDC'], - swappable: [], - symbol: 'USDC', - decimals: 6, - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - origin: ['RFQ.ETH'], - destination: ['RFQ.ETH'], - swappable: [], - symbol: 'ETH', - decimals: 18, - }, - }, - '1313161554': { - '0x07379565cD8B0CaE7c60Dc78e7f601b34AF2A21c': { - decimals: 18, - symbol: 'nUSD', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x4988a896b1227218e4A686fdE5EabdcAbd91571f', - '0xB12BFcA5A55806AaF64E99521918A4bf0fC40802', - ], - }, - '0x4988a896b1227218e4A686fdE5EabdcAbd91571f': { - decimals: 6, - symbol: 'USDT.e', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x07379565cD8B0CaE7c60Dc78e7f601b34AF2A21c', - '0xB12BFcA5A55806AaF64E99521918A4bf0fC40802', - ], - }, - '0xB12BFcA5A55806AaF64E99521918A4bf0fC40802': { - decimals: 6, - symbol: 'USDC.e', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x07379565cD8B0CaE7c60Dc78e7f601b34AF2A21c', - '0x4988a896b1227218e4A686fdE5EabdcAbd91571f', - ], - }, - '0xd80d8688b02B3FD3afb81cDb124F188BB5aD0445': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - }, - '1666600000': { - '0x0b5740c6b4a97f90eF2F0220651Cca420B868FfB': { - decimals: 18, - symbol: 'nETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [], - }, - '0x1852F70512298d56e9c8FDd905e02581E04ddb2a': { - decimals: 18, - symbol: 'synFRAX', - origin: ['synFRAX'], - destination: ['synFRAX'], - swappable: [], - }, - '0x28b42698Caf46B4B012CF38b6C75867E0762186D': { - decimals: 18, - symbol: 'synJEWEL', - origin: ['JEWEL'], - destination: ['JEWEL'], - swappable: ['0x72Cb10C6bfA5624dD07Ef608027E366bd690048F'], - }, - '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0x72Cb10C6bfA5624dD07Ef608027E366bd690048F': { - decimals: 18, - symbol: 'JEWEL', - origin: ['JEWEL'], - destination: ['JEWEL'], - swappable: ['0x28b42698Caf46B4B012CF38b6C75867E0762186D'], - }, - '0xA9cE83507D872C5e1273E745aBcfDa849DAA654F': { - decimals: 18, - symbol: 'xJEWEL', - origin: ['xJEWEL'], - destination: ['xJEWEL'], - swappable: [], - }, - '0xD9eAA386cCD65F30b77FF175F6b52115FE454fD6': { - decimals: 18, - symbol: 'AVAX', - origin: ['AVAX'], - destination: ['AVAX'], - swappable: ['0xb12c13e66AdE1F72f71834f2FC5082Db8C091358'], - }, - '0xE3c82A836Ec85311a433fBd9486EfAF4b1AFbF48': { - decimals: 18, - symbol: 'SDT', - origin: ['SDT'], - destination: ['SDT'], - swappable: [], - }, - '0xE55e19Fb4F2D85af758950957714292DAC1e25B2': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0xED2a7edd7413021d440b09D654f3b87712abAB66': { - decimals: 18, - symbol: 'nUSD', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [], - }, - '0xb12c13e66AdE1F72f71834f2FC5082Db8C091358': { - decimals: 18, - symbol: 'AVAX', - origin: ['AVAX'], - destination: ['AVAX'], - swappable: ['0xD9eAA386cCD65F30b77FF175F6b52115FE454fD6'], - }, - }, -} diff --git a/packages/synapse-constants/index.ts b/packages/synapse-constants/index.ts deleted file mode 100644 index 917d50c157..0000000000 --- a/packages/synapse-constants/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './constants/tokens/index' -export * as CHAINS from './constants/chains/index' -export * from './constants/types/index' -export * from './constants/assets/chains/index' -export * from './constants/assets/explorer/index' -export * from './constants/assets/icons/index' diff --git a/packages/synapse-constants/package.json b/packages/synapse-constants/package.json index f1f4789ef2..151e198c36 100644 --- a/packages/synapse-constants/package.json +++ b/packages/synapse-constants/package.json @@ -2,13 +2,18 @@ "name": "synapse-constants", "version": "1.3.24", "description": "This is an npm package that maintains all synapse constants", - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "typings": "dist/index.d.ts", + "main": "dist/cjs/index.js", + "module": "dist/esm/index.js", + "types": "dist/types/index.d.ts", + "license": "MIT", "files": [ "dist" ], + "repository": { + "type": "git", + "url": "git+https://github.com/synapsecns/sanguine.git" + }, + "homepage": "https://github.com/synapsecns/sanguine/blob/master/packages/synapse-constants/README.md", "scripts": { "test:coverage": "echo 'No tests defined.'", "ci:lint": " ", @@ -16,34 +21,30 @@ "build:slither": " ", "lint": " ", "lint:fix": "npm run lint -- --fix", - "lint:check": "eslint . --max-warnings=0", - "build": "node scripts/generateMaps.js && node scripts/findMissing.js", - "prepublish": "tsc", - "compile": "tsc && copyfiles -u 1 \"constants/assets/**/*.*\" dist/constants && webpack", - "maps:generate": "yarn build && yarn compile" + "lint:check": "eslint . --max-warnings=0 --config .eslintrc.cjs", + "prepare": "rollup -c --bundleConfigAsCjs", + "build": "rollup -c --bundleConfigAsCjs", + "prepublish": "yarn build", + "maps:generate": "node ./src/scripts/generateMaps.cjs && node ./src/scripts/findMissing.cjs && yarn build" }, - "author": "", - "license": "ISC", "dependencies": { - "@codecov/webpack-plugin": "^0.0.1-beta.10", - "copyfiles": "^2.4.1", + "@ethersproject/address": "5.7.0", + "@ethersproject/bignumber": "5.7.0", "ethers": "5.7.2", - "lodash": "^4.17.21" + "lodash": "^4.17.21", + "viem": "^2.21.6" }, "devDependencies": { - "babel-loader": "^9.1.3", + "@codecov/rollup-plugin": "^1.2.0", + "@rollup/plugin-commonjs": "^28.0.0", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^15.3.0", + "@rollup/plugin-terser": "^0.4.4", + "@rollup/plugin-url": "^8.0.2", "babel-plugin-transform-export-extensions": "^6.22.0", - "file-loader": "^6.2.0", - "image-minimizer-webpack-plugin": "^3.8.3", - "imagemin": "^8.0.1", - "imagemin-jpegtran": "^7.0.0", - "imagemin-optipng": "^8.0.0", - "imagemin-svgo": "^10.0.1", + "rollup": "^4.22.4", + "rollup-plugin-typescript2": "^0.36.0", "svg-inline-loader": "^0.8.2", - "ts-loader": "^9.5.1", - "typescript": "^5.3.3", - "url-loader": "^4.1.1", - "webpack": "^5.89.0", - "webpack-cli": "^5.1.4" + "typescript": "^5.3.3" } } diff --git a/packages/synapse-constants/rollup.config.js b/packages/synapse-constants/rollup.config.js new file mode 100644 index 0000000000..45444b7f2c --- /dev/null +++ b/packages/synapse-constants/rollup.config.js @@ -0,0 +1,53 @@ +import typescript from 'rollup-plugin-typescript2' +import { nodeResolve } from '@rollup/plugin-node-resolve' +import commonjs from '@rollup/plugin-commonjs' +import json from '@rollup/plugin-json' +import terser from '@rollup/plugin-terser' +import url from '@rollup/plugin-url' +import { codecovRollupPlugin } from '@codecov/rollup-plugin' + +import packageJson from './package.json' + +export default [ + { + input: 'src/index.ts', + output: [ + { + file: packageJson.main, + format: 'cjs', + }, + { + file: packageJson.module, + format: 'esm', + }, + ], + plugins: [ + nodeResolve({ + preferBuiltins: true, + }), + commonjs(), + json(), + typescript({ + tsconfig: './tsconfig.json', + declaration: true, + declarationDir: './dist/types', + useTsconfigDeclarationDir: true, + }), + terser(), + url({ + include: ['**/*.svg', '**/*.png', '**/*.jpg'], + limit: 20000, + emitFiles: false, + }), + codecovRollupPlugin({ + enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, + bundleName: 'synapse-constants', + uploadToken: process.env.CODECOV_TOKEN, + uploadOverrides: { + sha: process.env.GH_COMMIT_SHA, + }, + }), + ], + external: ['lodash', 'ethers'], + }, +] diff --git a/packages/synapse-constants/constants/bridgeMap.ts b/packages/synapse-constants/src/constants/bridgeMap.ts similarity index 100% rename from packages/synapse-constants/constants/bridgeMap.ts rename to packages/synapse-constants/src/constants/bridgeMap.ts diff --git a/packages/synapse-constants/constants/chains/index.ts b/packages/synapse-constants/src/constants/chains/index.ts similarity index 99% rename from packages/synapse-constants/constants/chains/index.ts rename to packages/synapse-constants/src/constants/chains/index.ts index d58ce89949..9fa24989d2 100644 --- a/packages/synapse-constants/constants/chains/index.ts +++ b/packages/synapse-constants/src/constants/chains/index.ts @@ -1,4 +1,4 @@ -import { Chain } from '../types' +import { Chain } from '../../types' import * as all from './master' export * from './master' diff --git a/packages/synapse-constants/constants/chains/master.ts b/packages/synapse-constants/src/constants/chains/master.ts similarity index 69% rename from packages/synapse-constants/constants/chains/master.ts rename to packages/synapse-constants/src/constants/chains/master.ts index 41fd997bbb..580fac3f09 100644 --- a/packages/synapse-constants/constants/chains/master.ts +++ b/packages/synapse-constants/src/constants/chains/master.ts @@ -1,48 +1,4 @@ -import arbitrumImg from '../assets/chains/arbitrum.svg' -import auroraImg from '../assets/chains/aurora.svg' -import avalancheImg from '../assets/chains/avalanche.svg' -import baseImg from '../assets/chains/base.svg' -import bobaImg from '../assets/chains/boba.svg' -import bscImg from '../assets/chains/bnb.svg' -import blastImg from '../assets/chains/blast.svg' -import cantoImg from '../assets/chains/canto.svg' -import cronosImg from '../assets/chains/cronos.svg' -import dfkImg from '../assets/chains/dfk.svg' -import dogechainImg from '../assets/chains/dogechain.svg' -import ethImg from '../assets/chains/ethereum.svg' -import fantomImg from '../assets/chains/fantom.svg' -import harmonyImg from '../assets/chains/harmony.svg' -import klaytnImg from '../assets/chains/klaytn.svg' -import metisImg from '../assets/chains/metis.svg' -import moonbeamImg from '../assets/chains/moonbeam.svg' -import moonriverImg from '../assets/chains/moonriver.svg' -import optimismImg from '../assets/chains/optimism.svg' -import polygonImg from '../assets/chains/polygon.svg' -import scrollImg from '../assets/chains/scroll.svg' -import lineaImg from '../assets/chains/linea.svg' -import ethExplorerImg from '../assets/explorer/etherscan.svg' -import arbitrumExplorerImg from '../assets/explorer/arbitrum.svg' -import bnbExplorerImg from '../assets/explorer/bscscan.svg' -import avalancheExplorerImg from '../assets/explorer/avalanche.svg' -import cantoExplorerImg from '../assets/explorer/canto.svg' -import optimismExplorerImg from '../assets/explorer/optimism.svg' -import polygonExplorerImg from '../assets/explorer/polygon.svg' -import dfkExplorerImg from '../assets/explorer/dfk-chain.svg' -import klaytynExplorerImg from '../assets/explorer/klaytn.svg' -import fantomExplorerImg from '../assets/explorer/fantom.svg' -import cronosExplorerImg from '../assets/explorer/cronos.svg' -import bobaExplorerImg from '../assets/explorer/boba.svg' -import metisExplorerImg from '../assets/explorer/metis.svg' -import auroraExplorerImg from '../assets/explorer/aurora.svg' -import harmonyExplorerImg from '../assets/explorer/harmony.svg' -import moonbeamExplorerImg from '../assets/explorer/moonbeam.svg' -import moonriverExplorerImg from '../assets/explorer/moonriver.svg' -import dogeExplorerImg from '../assets/explorer/dogechain.svg' -import baseExplorerImg from '../assets/explorer/basescan.svg' -import blastExplorerImg from '../assets/explorer/blast.svg' -import scrollExplorerImg from '../assets/explorer/scroll.svg' -import lineaExplorerImg from '../assets/explorer/linea.svg' -import { Chain } from '../types' +import { Chain } from '../../types' export const ETH: Chain = { priorityRank: 100, @@ -50,7 +6,8 @@ export const ETH: Chain = { chainSymbol: 'ETH', name: 'Ethereum', codeName: 'ethereum', - chainImg: ethImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/ethereum.4a372106.svg', layer: 1, rpcUrls: { primary: 'https://rpc.ankr.com/eth', @@ -58,7 +15,8 @@ export const ETH: Chain = { }, explorerUrl: 'https://etherscan.io', explorerName: 'Etherscan', - explorerImg: ethExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/ethereum.4a372106.svg', blockTime: 12000, nativeCurrency: { name: 'Ethereum', @@ -73,7 +31,8 @@ export const ARBITRUM: Chain = { id: 42161, chainSymbol: 'ARBITRUM', name: 'Arbitrum', - chainImg: arbitrumImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/arbitrum.8ddb1b22.svg', layer: 2, codeName: 'arbitrum', blockTime: 300, @@ -84,7 +43,8 @@ export const ARBITRUM: Chain = { nativeCurrency: { name: 'Ethereum', symbol: 'ETH', decimals: 18 }, explorerUrl: 'https://arbiscan.io', explorerName: 'Arbiscan', - explorerImg: arbitrumExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/arbitrum.8ddb1b22.svg', color: 'gray', } @@ -93,7 +53,8 @@ export const BNB: Chain = { id: 56, chainSymbol: 'BNB', name: 'BNB Chain', - chainImg: bscImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/bnb.5948fe5e.svg', altName: 'BNB', layer: 1, codeName: 'bsc', @@ -105,7 +66,8 @@ export const BNB: Chain = { nativeCurrency: { name: 'Binance Coin', symbol: 'BNB', decimals: 18 }, explorerUrl: 'https://bscscan.com', explorerName: 'BscScan', - explorerImg: bnbExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/bscscan.a50e7cfb.svg', color: 'yellow', } @@ -114,7 +76,8 @@ export const AVALANCHE: Chain = { id: 43114, chainSymbol: 'AVALANCHE', name: 'Avalanche', - chainImg: avalancheImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/avalanche.9d53cbf0.svg', layer: 1, codeName: 'avalanche', blockTime: 2000, @@ -125,7 +88,8 @@ export const AVALANCHE: Chain = { nativeCurrency: { name: 'Avax', symbol: 'AVAX', decimals: 18 }, explorerUrl: 'https://snowscan.xyz/', explorerName: 'SnowScan', - explorerImg: avalancheExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/snowscan.1d03dfbf.svg', color: 'red', } @@ -134,7 +98,8 @@ export const CANTO: Chain = { id: 7700, chainSymbol: 'CANTO', name: 'Canto', - chainImg: cantoImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/canto.cb85e14f.svg', layer: 1, codeName: 'canto', blockTime: 6000, @@ -145,7 +110,8 @@ export const CANTO: Chain = { nativeCurrency: { name: 'Canto', symbol: 'CANTO', decimals: 18 }, explorerUrl: 'https://tuber.build/', explorerName: 'Canto Explorer', - explorerImg: cantoExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/snowscan.1d03dfbf.svg', color: 'green', } @@ -154,7 +120,8 @@ export const OPTIMISM: Chain = { id: 10, chainSymbol: 'OPTIMISM', name: 'Optimism', - chainImg: optimismImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/optimism.84d4f0ef.svg', layer: 2, codeName: 'optimism', blockTime: 2000, @@ -165,7 +132,8 @@ export const OPTIMISM: Chain = { nativeCurrency: { name: 'Ethereum', symbol: 'ETH', decimals: 18 }, explorerUrl: 'https://optimistic.etherscan.io', explorerName: 'Optimism Explorer', - explorerImg: optimismExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/optimism.84d4f0ef.svg', color: 'red', } @@ -174,7 +142,8 @@ export const POLYGON: Chain = { id: 137, chainSymbol: 'POLYGON', name: 'Polygon', - chainImg: polygonImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/polygon.237cd2b6.svg', layer: 2, codeName: 'polygon', blockTime: 2000, @@ -185,7 +154,8 @@ export const POLYGON: Chain = { nativeCurrency: { name: 'Matic', symbol: 'MATIC', decimals: 18 }, explorerUrl: 'https://polygonscan.com', explorerName: 'PolygonScan', - explorerImg: polygonExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/polygon.237cd2b6.svg', color: 'purple', } @@ -194,7 +164,8 @@ export const DFK: Chain = { id: 53935, chainSymbol: 'DFK', name: 'DFK Chain', - chainImg: dfkImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/dfk.2bd1f0e4.svg', layer: 1, codeName: 'dfk', blockTime: 2000, @@ -205,7 +176,8 @@ export const DFK: Chain = { nativeCurrency: { name: 'Jewel', symbol: 'JEWEL', decimals: 18 }, explorerUrl: 'https://subnets.avax.network/defi-kingdoms', explorerName: 'DFK Subnet Explorer', - explorerImg: dfkExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/dfk.2bd1f0e4.svg', color: 'lime', } @@ -214,7 +186,8 @@ export const KLAYTN: Chain = { id: 8217, chainSymbol: 'KLAYTN', name: 'Klaytn', - chainImg: klaytnImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/klaytn.59495fbb.svg', layer: 1, codeName: 'klaytn', blockTime: 1000, @@ -225,7 +198,8 @@ export const KLAYTN: Chain = { nativeCurrency: { name: 'Klaytn', symbol: 'KLAY', decimals: 18 }, explorerUrl: 'https://scope.klaytn.com', explorerName: 'Klaytn Explorer', - explorerImg: klaytynExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/klaytn.59495fbb.svg', color: 'orange', } @@ -234,7 +208,8 @@ export const FANTOM: Chain = { id: 250, chainSymbol: 'FANTOM', name: 'Fantom', - chainImg: fantomImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/fantom.1e444dad.svg', layer: 1, codeName: 'fantom', blockTime: 1000, @@ -245,7 +220,8 @@ export const FANTOM: Chain = { nativeCurrency: { name: 'Fantom', symbol: 'FTM', decimals: 18 }, explorerUrl: 'https://ftmscan.com', explorerName: 'FTMScan', - explorerImg: fantomExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/fantom.1e444dad.svg', color: 'blue', } @@ -254,7 +230,8 @@ export const CRONOS: Chain = { id: 25, chainSymbol: 'CRONOS', name: 'Cronos', - chainImg: cronosImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/cronos.b06f8311.svg', layer: 1, codeName: 'cronos', blockTime: 6000, @@ -265,7 +242,8 @@ export const CRONOS: Chain = { nativeCurrency: { name: 'Cronos', symbol: 'CRO', decimals: 18 }, explorerUrl: 'https://cronoscan.com', explorerName: 'CronoScan', - explorerImg: cronosExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/cronos.b06f8311.svg', color: 'gray', } @@ -274,7 +252,8 @@ export const BOBA: Chain = { id: 288, chainSymbol: 'BOBA', name: 'Boba Chain', - chainImg: bobaImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/boba.2072e50b.svg', layer: 2, codeName: 'boba', blockTime: 1000, @@ -285,7 +264,8 @@ export const BOBA: Chain = { nativeCurrency: { name: 'Ethereum', symbol: 'ETH', decimals: 18 }, explorerUrl: 'https://bobascan.com', explorerName: 'Boba Explorer', - explorerImg: bobaExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/boba.2072e50b.svg', color: 'lime', } @@ -294,7 +274,8 @@ export const METIS: Chain = { id: 1088, chainSymbol: 'METIS', name: 'Metis', - chainImg: metisImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/metis.90b6abf0.svg', layer: 2, codeName: 'metis', blockTime: 4000, @@ -305,7 +286,8 @@ export const METIS: Chain = { nativeCurrency: { name: 'Metis', symbol: 'METIS', decimals: 18 }, explorerUrl: 'https://andromeda-explorer.metis.io', explorerName: 'Metis Explorer', - explorerImg: metisExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/metis.90b6abf0.svg', color: 'teal', } @@ -314,7 +296,8 @@ export const AURORA: Chain = { id: 1313161554, chainSymbol: 'AURORA', name: 'Aurora', - chainImg: auroraImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/aurora.5a46037d.svg', layer: 1, codeName: 'aurora', blockTime: 1000, @@ -325,7 +308,8 @@ export const AURORA: Chain = { nativeCurrency: { name: 'Ethereum', symbol: 'ETH', decimals: 18 }, explorerUrl: 'https://explorer.mainnet.aurora.dev', explorerName: 'Aurora Explorer', - explorerImg: auroraExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/aurora.5a46037d.svg', color: 'lime', } @@ -334,7 +318,8 @@ export const HARMONY: Chain = { id: 1666600000, chainSymbol: 'HARMONY', name: 'Harmony', - chainImg: harmonyImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/harmony.af12f77e.svg', layer: 1, codeName: 'harmony', blockTime: 2000, @@ -345,7 +330,8 @@ export const HARMONY: Chain = { nativeCurrency: { name: 'Harmony One', symbol: 'ONE', decimals: 18 }, explorerUrl: 'https://explorer.harmony.one', explorerName: 'Harmony Explorer', - explorerImg: harmonyExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/harmony.af12f77e.svg', color: 'cyan', } @@ -354,7 +340,8 @@ export const MOONBEAM: Chain = { id: 1284, chainSymbol: 'MOONBEAM', name: 'Moonbeam', - chainImg: moonbeamImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/moonbeam.284ab9b4.svg', layer: 1, codeName: 'moonbeam', blockTime: 12000, @@ -365,7 +352,8 @@ export const MOONBEAM: Chain = { nativeCurrency: { name: 'Glimmer', symbol: 'GLMR', decimals: 18 }, explorerUrl: 'https://moonbeam.moonscan.io', explorerName: 'Moonbeam Explorer', - explorerImg: moonbeamExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/moonbeam.284ab9b4.svg', color: 'teal', } @@ -374,7 +362,8 @@ export const MOONRIVER: Chain = { id: 1285, chainSymbol: 'MOONRIVER', name: 'Moonriver', - chainImg: moonriverImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/moonriver.3fb35010.svg', layer: 1, codeName: 'moonriver', blockTime: 12000, @@ -385,7 +374,8 @@ export const MOONRIVER: Chain = { nativeCurrency: { name: 'Moonriver', symbol: 'MOVR', decimals: 18 }, explorerUrl: 'https://moonriver.moonscan.io', explorerName: 'Moonriver Explorer', - explorerImg: moonriverExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/moonriver.3fb35010.svg', color: 'purple', } @@ -394,7 +384,8 @@ export const DOGE: Chain = { id: 2000, chainSymbol: 'DOGE', name: 'Dogechain', - chainImg: dogechainImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/dogechain.36935650.svg', layer: 1, codeName: 'dogechain', blockTime: 2000, @@ -405,7 +396,8 @@ export const DOGE: Chain = { nativeCurrency: { name: 'DOGE', symbol: 'DOGE', decimals: 18 }, explorerUrl: 'https://explorer.dogechain.dog', explorerName: 'Dogechain Explorer', - explorerImg: dogeExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/dogechain.36935650.svg', color: 'purple', } @@ -415,7 +407,8 @@ export const BASE: Chain = { chainSymbol: 'ETH', name: 'Base', codeName: 'base', - chainImg: baseImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/base.d919fbef.svg', layer: 2, rpcUrls: { primary: 'https://base.blockpi.network/v1/rpc/public', @@ -423,7 +416,8 @@ export const BASE: Chain = { }, explorerUrl: 'https://basescan.org', explorerName: 'BaseScan', - explorerImg: baseExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/base.d919fbef.svg', blockTime: 3000, nativeCurrency: { name: 'Ether', @@ -439,7 +433,8 @@ export const BLAST: Chain = { chainSymbol: 'ETH', name: 'Blast', codeName: 'blast', - chainImg: blastImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/blast.e39807f8.svg', layer: 2, rpcUrls: { primary: 'https://rpc.blast.io', @@ -447,7 +442,8 @@ export const BLAST: Chain = { }, explorerUrl: 'https://blastscan.io', explorerName: 'Blastscan', - explorerImg: blastExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/blast.e39807f8.svg', blockTime: 3000, nativeCurrency: { name: 'Ether', @@ -463,7 +459,8 @@ export const SCROLL: Chain = { chainSymbol: 'SCROLL', name: 'Scroll', codeName: 'scroll', - chainImg: scrollImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/scroll.a805c122.svg', layer: 2, rpcUrls: { primary: 'https://rpc.scroll.io', @@ -471,7 +468,8 @@ export const SCROLL: Chain = { }, explorerUrl: 'https://scrollscan.com/', explorerName: 'Scrollscan', - explorerImg: scrollExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/scroll.a805c122.svg', blockTime: 3000, nativeCurrency: { name: 'Ether', @@ -487,7 +485,8 @@ export const LINEA: Chain = { chainSymbol: 'LINEA', name: 'Linea', codeName: 'linea', - chainImg: lineaImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/linea.e476f2ad.svg', layer: 2, rpcUrls: { primary: 'https://linea.blockpi.network/v1/rpc/public', @@ -495,7 +494,8 @@ export const LINEA: Chain = { }, explorerUrl: 'https://lineascan.build/', explorerName: 'LineaScan', - explorerImg: lineaExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/scroll.a805c122.svg', blockTime: 3000, nativeCurrency: { name: 'Ether', diff --git a/packages/synapse-constants/constants/index.ts b/packages/synapse-constants/src/constants/index.ts similarity index 58% rename from packages/synapse-constants/constants/index.ts rename to packages/synapse-constants/src/constants/index.ts index 1f6865e221..4ad4121153 100644 --- a/packages/synapse-constants/constants/index.ts +++ b/packages/synapse-constants/src/constants/index.ts @@ -1,4 +1,3 @@ export * as TOKENS from './tokens' export * as CHAINS from './chains' -export * from './types' -export * from './assets' +export * from '../types' diff --git a/packages/synapse-constants/constants/minichef.ts b/packages/synapse-constants/src/constants/minichef.ts similarity index 100% rename from packages/synapse-constants/constants/minichef.ts rename to packages/synapse-constants/src/constants/minichef.ts diff --git a/packages/synapse-constants/constants/tokens/auxilliary.ts b/packages/synapse-constants/src/constants/tokens/auxilliary.ts similarity index 71% rename from packages/synapse-constants/constants/tokens/auxilliary.ts rename to packages/synapse-constants/src/constants/tokens/auxilliary.ts index dfc7407607..fe9d3c4a6b 100644 --- a/packages/synapse-constants/constants/tokens/auxilliary.ts +++ b/packages/synapse-constants/src/constants/tokens/auxilliary.ts @@ -1,10 +1,4 @@ -import avaxLogo from '../assets/icons/avax.svg' -import avwethLogo from '../assets/icons/avweth.svg' -import ethLogo from '../assets/icons/eth.svg' -import mimLogo from '../assets/icons/mim.svg' -import usdcLogo from '../assets/icons/usdc.svg' -import usdtLogo from '../assets/icons/usdt.svg' -import { Token } from '../types' +import { Token } from '../../types' import * as CHAINS from '../chains/master' export const AVWETH = new Token({ @@ -14,7 +8,7 @@ export const AVWETH = new Token({ decimals: 18, symbol: 'AVWETH', name: 'Aave Wrapped ETH', - logo: avwethLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/weth.19fa93ab.svg', swapableType: 'ETH', color: 'cyan', priorityRank: 2, @@ -30,7 +24,7 @@ export const KLAYTN_oUSDT = new Token({ }, symbol: 'orbitUSDT', name: 'Orbit Bridged USDT', - logo: usdtLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdt.3c9cd2f8.svg', swapableType: 'KLAYTN_USDT', swapableOn: [CHAINS.KLAYTN.id], priorityRank: 6, @@ -45,7 +39,7 @@ export const MIM = new Token({ decimals: 18, symbol: 'MIM', name: 'Magic Internet Money', - logo: mimLogo, + logo: 'https://docs.abracadabra.money/~gitbook/image?url=https%3A%2F%2F2388475231-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-Mc9U0yE30Tc9xb3mVGA%252F-McF-MUtKQnjF8iBPrv5%252F-McF8_XaAL4kyHrICXUw%252FMIM%2520Logo%2520PNG.png%3Falt%3Dmedia%26token%3D12b05d37-765f-494d-809b-4deab52bd212&width=768&dpr=2&quality=100&sign=7d2ede98&sv=1', swapableType: 'USD', color: 'indigo', priorityRank: 6, @@ -59,7 +53,7 @@ export const MULTIAVAX = new Token({ decimals: 18, symbol: 'multiAVAX', name: 'AnySwap Wrapped AVAX', - logo: avaxLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/avax.9d53cbf0.svg', swapableType: 'AVAX', swapableOn: [CHAINS.HARMONY.id], color: 'red', @@ -77,7 +71,7 @@ export const FANTOMUSDC = new Token({ }, symbol: 'USDC', name: 'USD Coin', - logo: usdcLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdc.d5dcb030.svg', swapableType: 'USD', swapableOn: [], color: 'blue', @@ -95,7 +89,7 @@ export const FANTOMUSDT = new Token({ }, symbol: 'USDT', name: 'USD Tether', - logo: usdtLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdt.3c9cd2f8.svg', color: 'lime', swapableType: 'USD', swapableOn: [], @@ -111,7 +105,7 @@ export const FANTOMETH = new Token({ decimals: 18, symbol: 'ETH', name: 'Ethereum', - logo: ethLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/eth.b3692688.svg', isNative: true, swapableType: 'ETH', color: 'sky', diff --git a/packages/synapse-constants/constants/tokens/bridgeable.ts b/packages/synapse-constants/src/constants/tokens/bridgeable.ts similarity index 86% rename from packages/synapse-constants/constants/tokens/bridgeable.ts rename to packages/synapse-constants/src/constants/tokens/bridgeable.ts index 3641b8a8aa..9f160fcd3a 100644 --- a/packages/synapse-constants/constants/tokens/bridgeable.ts +++ b/packages/synapse-constants/src/constants/tokens/bridgeable.ts @@ -1,45 +1,4 @@ -import ageurLogo from '../assets/icons/ageur.svg' -import avaxLogo from '../assets/icons/avax.svg' -import btcLogo from '../assets/icons/btc.svg' -import busdLogo from '../assets/icons/busd.svg' -import crvusdLogo from '../assets/icons/crvusd.svg' -import linkLogo from '../assets/icons/link.svg' -import daiLogo from '../assets/icons/dai.svg' -import dogLogo from '../assets/icons/dog.svg' -import ethLogo from '../assets/icons/eth.svg' -import fraxLogo from '../assets/icons/frax.svg' -import ftmLogo from '../assets/icons/ftm.svg' -import gmxLogo from '../assets/icons/gmx.svg' -import h2oLogo from '../assets/icons/h2o.svg' -import highLogo from '../assets/icons/highstreet.svg' -import hyperjumpLogo from '../assets/icons/hyperjump.svg' -import jewelLogo from '../assets/icons/jewel.png' -import klayLogo from '../assets/icons/klay.svg' -import l2daoLogo from '../assets/icons/l2dao.svg' -import maticLogo from '../assets/icons/matic.svg' -import movrLogo from '../assets/icons/movr.svg' -import nethLogo from '../assets/icons/neth.svg' -import newoLogo from '../assets/icons/newo.svg' -import nfdLogo from '../assets/icons/nfd.svg' -import noteLogo from '../assets/icons/note.svg' -import nusdLogo from '../assets/icons/nusd.svg' -import ohmLogo from '../assets/icons/ohm.svg' -import pepeLogo from '../assets/icons/pepe.svg' -import plsLogo from '../assets/icons/pls.svg' -import sdtLogo from '../assets/icons/sdt.svg' -import sfiLogo from '../assets/icons/sfi.svg' -import solarbeamLogo from '../assets/icons/solar.svg' -import susdLogo from '../assets/icons/susd.svg' -import synapseLogo from '../assets/icons/syn.svg' -import spectralLogo from '../assets/icons/spectral.svg' -import unidexLogo from '../assets/icons/unidex.svg' -import usdcLogo from '../assets/icons/usdc.svg' -import usdtLogo from '../assets/icons/usdt.svg' -import vstaLogo from '../assets/icons/vsta.svg' -import wbtcLogo from '../assets/icons/wbtc.svg' -import wethLogo from '../assets/icons/weth.svg' -import usdbLogo from '../assets/icons/usdb.svg' -import { Token } from '../types' +import { Token } from '../../types' import * as CHAINS from '../chains/master' const zeroAddress = '0x0000000000000000000000000000000000000000' @@ -64,7 +23,7 @@ export const GOHM = new Token({ decimals: 18, symbol: 'gOHM', name: 'Olympus DAO', - logo: ohmLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/ohm.1b779b45.svg', swapableType: 'OHM', color: 'gray', visibilityRank: 40, @@ -81,7 +40,7 @@ export const LINK = new Token({ decimals: 18, symbol: 'LINK', name: 'ChainLink Token', - logo: linkLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/link.bdd944c9.svg', swapableType: 'LINK', color: 'blue', priorityRank: 6, @@ -97,7 +56,7 @@ export const HIGH = new Token({ decimals: 18, symbol: 'HIGH', name: 'Highstreet', - logo: highLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/highstreet.e8060cc0.svg', swapableType: 'HIGH', color: 'cyan', priorityRank: 6, @@ -114,7 +73,7 @@ export const JUMP = new Token({ decimals: 18, symbol: 'JUMP', name: 'HyperJump', - logo: hyperjumpLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/hyperjump.e48ce883.svg', docUrl: '', swapableType: 'JUMP', color: 'cyan', @@ -130,7 +89,7 @@ export const SFI = new Token({ decimals: 18, symbol: 'SFI', name: 'Saffron Finance', - logo: sfiLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/sfi.4e35fbac.svg', docUrl: '', swapableType: 'SFI', color: 'red', @@ -148,7 +107,7 @@ export const DOG = new Token({ decimals: 18, symbol: 'DOG', name: 'The Doge NFT', - logo: dogLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/dog.b3513f9b.svg', docUrl: '', swapableType: 'DOG', color: 'yellow', @@ -167,7 +126,7 @@ export const NFD = new Token({ decimals: 18, symbol: 'NFD', name: 'Feisty Doge', - logo: nfdLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/nfd.1212b18d.svg', docUrl: '', swapableType: 'NFD', color: 'yellow', @@ -184,7 +143,7 @@ export const SOLAR = new Token({ decimals: 18, symbol: 'veSOLAR', name: 'Vested SolarBeam', - logo: solarbeamLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/solar.add1885e.svg', docUrl: '', swapableType: 'SOLAR', color: 'orange', @@ -203,7 +162,7 @@ export const GMX = new Token({ decimals: 18, symbol: 'GMX', name: 'GMX', - logo: gmxLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/gmx.0e372310.svg', docUrl: '', swapableType: 'GMX', priorityRank: 6, @@ -223,7 +182,7 @@ export const SDT = new Token({ decimals: 18, symbol: 'SDT', name: 'Stake DAO', - logo: sdtLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/sdt.c37b0b67.svg', docUrl: '', swapableType: 'SDT', color: 'gray', @@ -241,7 +200,7 @@ export const NEWO = new Token({ decimals: 18, symbol: 'NEWO', name: 'New Order', - logo: newoLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/newo.3c9ae85e.svg', docUrl: '', swapableType: 'NEWO', color: 'yellow', @@ -258,7 +217,7 @@ export const PEPE = new Token({ decimals: 18, symbol: 'PEPE', name: 'Pepe', - logo: pepeLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/pepe.5da81140.svg', swapableType: 'PEPE', priorityRank: 6, routeSymbol: 'PEPE', @@ -274,7 +233,7 @@ export const VSTA = new Token({ decimals: 18, symbol: 'VSTA', name: 'Vesta', - logo: vstaLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/vsta.56aa9f41.svg', docUrl: '', swapableType: 'VSTA', color: 'gray', @@ -297,7 +256,7 @@ export const H2O = new Token({ decimals: 18, symbol: 'H2O', name: 'H2O', - logo: h2oLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/h2o.108c0951.svg', docUrl: '', swapableType: 'H2O', color: 'cyan', @@ -314,7 +273,7 @@ export const L2DAO = new Token({ decimals: 18, symbol: 'L2DAO', name: 'Layer2DAO', - logo: l2daoLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/l2dao.0d1dd411.svg', docUrl: '', swapableType: 'L2DAO', color: 'cyan', @@ -331,7 +290,7 @@ export const PLS = new Token({ decimals: 18, symbol: 'PLS', name: 'Plutus', - logo: plsLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/pls.a97f40f2.svg', docUrl: '', swapableType: 'PLS', color: 'green', @@ -349,7 +308,7 @@ export const AGEUR = new Token({ decimals: 18, symbol: 'agEUR', name: 'Angle Euro', - logo: ageurLogo, + logo: 'https://45a97b3d.sanguine-fe.pages.dev/_next/static/media/ageur.588833db.svg', docUrl: '', swapableType: 'AGEUR', color: 'yellow', @@ -369,7 +328,7 @@ export const UNIDX = new Token({ decimals: 18, symbol: 'UNIDX', name: 'Unidex', - logo: unidexLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/unidex.29d556df.svg', docUrl: '', swapableType: 'UNIDX', color: 'gray', @@ -386,7 +345,7 @@ export const BUSD = new Token({ decimals: 18, symbol: 'BUSD', name: 'Binance USD', - logo: busdLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/busd.e8bb5032.svg', swapableType: 'BUSD', swapableOn: [CHAINS.BNB.id], color: 'yellow', @@ -437,7 +396,7 @@ export const USDC = new Token({ }, symbol: 'USDC', name: 'USD Coin', - logo: usdcLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdc.d5dcb030.svg', swapableType: 'USD', swapableOn: [ CHAINS.BNB.id, @@ -469,7 +428,7 @@ export const METISUSDC = new Token({ }, symbol: 'm.USDC', name: 'Metis USD Coin', - logo: usdcLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdc.d5dcb030.svg', swapableType: 'USD', swapableOn: [CHAINS.METIS.id], color: 'blue', @@ -510,7 +469,7 @@ export const USDT = new Token({ }, symbol: 'USDT', name: 'USD Tether', - logo: usdtLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdt.3c9cd2f8.svg', color: 'lime', swapableType: 'USD', swapableOn: [ @@ -548,7 +507,7 @@ export const DAI = new Token({ decimals: 18, symbol: 'DAI', name: 'Dai', - logo: daiLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/dai.ba6d3142.svg', swapableType: 'USD', swapableOn: [ CHAINS.ETH.id, @@ -577,7 +536,7 @@ export const WBTC = new Token({ }, symbol: 'WBTC', name: 'Wrapped BTC', - logo: wbtcLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/wbtc.d5fa58cc.svg', swapableType: 'WBTC', color: 'orange', priorityRank: 3, @@ -592,7 +551,7 @@ export const WETHE = new Token({ decimals: 18, symbol: 'WETH.e', name: 'Wrapped ETH', - logo: wethLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/weth.19fa93ab.svg', swapableType: 'ETH', swapableOn: [CHAINS.AVALANCHE.id], color: 'sky', @@ -607,7 +566,7 @@ export const ONEETH = new Token({ decimals: 18, symbol: '1ETH', name: 'Harmony ETH', - logo: wethLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/weth.19fa93ab.svg', swapableType: 'ETH', swapableOn: [CHAINS.HARMONY.id], color: 'sky', @@ -639,7 +598,7 @@ export const SYN = new Token({ decimals: 18, symbol: 'SYN', name: 'Synapse', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', swapableType: 'SYN', color: 'purple', visibilityRank: 90, @@ -658,7 +617,7 @@ export const FRAX = new Token({ decimals: 18, symbol: 'FRAX', name: 'Frax', - logo: fraxLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/frax.0f0670da.svg', swapableType: 'FRAX', color: 'gray', priorityRank: 6, @@ -676,7 +635,7 @@ export const SYNFRAX = new Token({ decimals: 18, symbol: 'synFRAX', name: 'Synapse Frax', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', swapableType: 'FRAX', color: 'gray', priorityRank: 4, @@ -703,7 +662,7 @@ export const NUSD = new Token({ decimals: 18, symbol: 'nUSD', name: 'Synapse nUSD', - logo: nusdLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/nusd.c7a22fdb.svg', swapableType: 'USD', swapableOn: [ CHAINS.BNB.id, @@ -733,7 +692,7 @@ export const NOTE = new Token({ decimals: 18, symbol: 'NOTE', name: 'Canto Note', - logo: noteLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/note.d5246fbd.svg', swapableType: 'USD', swapableOn: [CHAINS.CANTO.id], color: 'green', @@ -759,7 +718,7 @@ export const NETH = new Token({ decimals: 18, symbol: 'nETH', name: 'Synapse nETH', - logo: nethLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/neth.7c854d0f.svg', swapableType: 'ETH', swapableOn: [ CHAINS.OPTIMISM.id, @@ -794,7 +753,7 @@ export const ETH = new Token({ decimals: 18, symbol: 'ETH', name: 'Ethereum', - logo: ethLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/eth.b3692688.svg', isNative: true, swapableType: 'ETH', color: 'sky', @@ -819,7 +778,7 @@ export const MOVR = new Token({ decimals: 18, symbol: 'MOVR', name: 'MOVR', - logo: movrLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/movr.3fb35010.svg', isNative: true, swapableType: 'MOVR', color: 'purple', @@ -839,7 +798,7 @@ export const AVAX = new Token({ decimals: 18, symbol: 'AVAX', name: 'AVAX', - logo: avaxLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/avax.9d53cbf0.svg', isNative: true, swapableType: 'AVAX', color: 'red', @@ -856,7 +815,7 @@ export const WMOVR = new Token({ decimals: 18, symbol: 'MOVR', name: 'Wrapped MOVR', - logo: movrLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/movr.3fb35010.svg', swapableType: 'MOVR', color: 'purple', priorityRank: 3, @@ -870,7 +829,7 @@ export const WAVAX = new Token({ decimals: 18, symbol: 'AVAX', name: 'Wrapped AVAX', - logo: avaxLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/avax.9d53cbf0.svg', swapableType: 'AVAX', color: 'red', visibilityRank: 90, @@ -890,7 +849,7 @@ export const JEWEL = new Token({ decimals: 18, symbol: 'JEWEL', name: 'JEWEL', - logo: jewelLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/jewel.b2e41862.svg', color: 'lime', isNative: true, swapableType: 'JEWEL', @@ -906,7 +865,7 @@ export const WJEWEL = new Token({ decimals: 18, symbol: 'WJEWEL', name: 'Wrapped JEWEL', - logo: jewelLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/jewel.b2e41862.svg', swapableType: 'JEWEL', swapableOn: [CHAINS.HARMONY.id], color: 'lime', @@ -921,7 +880,7 @@ export const SYNJEWEL = new Token({ decimals: 18, symbol: 'synJEWEL', name: 'synJEWEL', - logo: jewelLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/jewel.b2e41862.svg', swapableType: 'JEWEL', swapableOn: [CHAINS.HARMONY.id], color: 'lime', @@ -937,7 +896,7 @@ export const XJEWEL = new Token({ decimals: 18, symbol: 'xJEWEL', name: 'xJEWEL', - logo: jewelLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/jewel.b2e41862.svg', swapableType: 'XJEWEL', color: 'lime', priorityRank: 3, @@ -956,7 +915,7 @@ export const USDCe = new Token({ decimals: 6, symbol: 'USDC.e', name: 'Bridged USDC', - logo: usdcLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdc.d5dcb030.svg', swapableType: 'USD', color: 'blue', swapableOn: [ @@ -978,7 +937,7 @@ export const USDTe = new Token({ decimals: 6, symbol: 'USDT.e', name: 'Bridged USDT', - logo: usdtLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdt.3c9cd2f8.svg', swapableType: 'USD', swapableOn: [CHAINS.AVALANCHE.id], visibilityRank: 100, @@ -996,7 +955,7 @@ export const SUSD = new Token({ }, symbol: 'sUSD', name: 'Synth sUSD', - logo: susdLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/susd.3aa0b6c4.svg', color: 'purple', swapableType: 'USD', swapableOn: [CHAINS.OPTIMISM.id], @@ -1019,7 +978,7 @@ export const WSOHM = new Token({ }, symbol: 'wsOHM', name: 'Wrapped sOHM', - logo: ohmLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/ohm.1b779b45.svg', color: 'gray', swapableType: 'OHM', visibilityRank: 40, @@ -1035,7 +994,7 @@ export const ONEDAI = new Token({ decimals: 18, symbol: '1DAI', name: 'Harmony Dai Stablecoin', - logo: daiLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/dai.ba6d3142.svg', swapableType: 'USD', swapableOn: [CHAINS.HARMONY.id], color: 'yellow', @@ -1054,7 +1013,7 @@ export const ONEUSDC = new Token({ }, symbol: '1USDC', name: 'Harmony USD Coin', - logo: usdcLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdc.d5dcb030.svg', swapableType: 'USD', swapableOn: [CHAINS.HARMONY.id], color: 'blue', @@ -1071,7 +1030,7 @@ export const ONEUSDT = new Token({ }, symbol: '1USDT', name: 'Harmony USD Tether', - logo: usdtLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdt.3c9cd2f8.svg', color: 'lime', swapableType: 'USD', swapableOn: [CHAINS.HARMONY.id], @@ -1093,7 +1052,7 @@ export const BTCB = new Token({ }, symbol: 'BTC.b', name: 'Bitcoin', - logo: btcLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/btc.63077225.svg', swapableType: 'BTC.b', color: 'orange', priorityRank: 3, @@ -1108,7 +1067,7 @@ export const DAIE = new Token({ decimals: 18, symbol: 'DAI.e', name: 'Dai.e Token', - logo: daiLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/dai.ba6d3142.svg', swapableType: 'USD', swapableOn: [CHAINS.AVALANCHE.id], color: 'yellow', @@ -1128,7 +1087,7 @@ export const KLAY = new Token({ }, symbol: 'KLAY', name: 'Klaytn', - logo: klayLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/klay.a50be928.svg', isNative: true, swapableType: 'KLAY', color: 'red', @@ -1146,7 +1105,7 @@ export const WKLAY = new Token({ }, symbol: 'WKLAY', name: 'Wrapped Klaytn', - logo: klayLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/klay.a50be928.svg', swapableType: 'WKLAY', color: 'red', priorityRank: 3, @@ -1161,7 +1120,7 @@ export const MATIC = new Token({ decimals: 18, symbol: 'MATIC', name: 'MATIC', - logo: maticLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/matic.237cd2b6.svg', isNative: true, swapableType: 'MATIC', color: 'blue', @@ -1178,7 +1137,7 @@ export const WMATIC = new Token({ decimals: 18, symbol: 'WMATIC', name: 'WMATIC', - logo: maticLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/matic.237cd2b6.svg', swapableType: 'MATIC', color: 'blue', visibilityRank: 90, @@ -1194,7 +1153,7 @@ export const FTM = new Token({ decimals: 18, symbol: 'FTM', name: 'Fantom', - logo: ftmLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/ftm.af71a88f.svg', swapableType: 'FTM', swapableOn: [CHAINS.FANTOM.id], color: 'blue', @@ -1210,7 +1169,7 @@ export const WFTM = new Token({ decimals: 18, symbol: 'WFTM', name: 'Wrapped Fantom', - logo: ftmLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/ftm.af71a88f.svg', swapableType: 'FTM', swapableOn: [CHAINS.FANTOM.id], color: 'blue', @@ -1234,7 +1193,7 @@ export const WETH = new Token({ decimals: 18, symbol: 'WETH', name: 'Wrapped ETH', - logo: wethLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/weth.19fa93ab.svg', swapableType: 'ETH', color: 'sky', priorityRank: 3, @@ -1252,7 +1211,7 @@ export const CRVUSDC = new Token({ swapExceptions: {}, symbol: 'crvUSD', name: 'Curve.fi USD', - logo: crvusdLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/crvusd.e65e6479.svg', swapableType: 'USD', swapableOn: [], color: 'yellow', @@ -1272,7 +1231,7 @@ export const USDBC = new Token({ swapExceptions: {}, symbol: 'USDbC', name: 'USD Base Coin', - logo: usdcLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdc.d5dcb030.svg', swapableType: 'USD', swapableOn: [], color: 'blue', @@ -1292,7 +1251,7 @@ export const USDB = new Token({ swapExceptions: {}, symbol: 'USDB', name: 'Blast Rebasing USD', - logo: usdbLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdb.33190604.svg', swapableType: 'USD', swapableOn: [], color: 'blue', @@ -1312,7 +1271,7 @@ export const SPECTRAL = new Token({ }, symbol: 'SPEC', name: 'Spectral Token', - logo: spectralLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/spectral.6d51750c.svg', swapableType: 'SPEC', color: 'blue', routeSymbol: 'SPEC', diff --git a/packages/synapse-constants/constants/tokens/deprecated.ts b/packages/synapse-constants/src/constants/tokens/deprecated.ts similarity index 79% rename from packages/synapse-constants/constants/tokens/deprecated.ts rename to packages/synapse-constants/src/constants/tokens/deprecated.ts index 6ff49c7566..6d76314adf 100644 --- a/packages/synapse-constants/constants/tokens/deprecated.ts +++ b/packages/synapse-constants/src/constants/tokens/deprecated.ts @@ -1,6 +1,4 @@ -import usdbLogo from '../assets/icons/usdc.svg' -import fusdtLogo from '../assets/icons/usdt.svg' -import { Token } from '../types' +import { Token } from '../../types' import * as CHAINS from '../chains/master' export const USDB = new Token({ @@ -10,7 +8,7 @@ export const USDB = new Token({ decimals: 18, symbol: 'USDB', name: 'USDB', - logo: usdbLogo, + logo: 'https://bscscan.com/token/images/usdb_32.png', docUrl: '', swapableType: 'USDB', priorityRank: 6, @@ -26,7 +24,7 @@ export const FUSDT = new Token({ }, symbol: 'fUSDT', name: 'Frapped USDT', - logo: fusdtLogo, + logo: 'https://ftmscan.com/token/images/frappedusdt_32.png', color: 'lime', swapableType: 'USD', swapableOn: [CHAINS.FANTOM.id], diff --git a/packages/synapse-constants/constants/tokens/index.ts b/packages/synapse-constants/src/constants/tokens/index.ts similarity index 98% rename from packages/synapse-constants/constants/tokens/index.ts rename to packages/synapse-constants/src/constants/tokens/index.ts index 7b8326afd9..531e27c61b 100644 --- a/packages/synapse-constants/constants/tokens/index.ts +++ b/packages/synapse-constants/src/constants/tokens/index.ts @@ -1,6 +1,6 @@ import _ from 'lodash' -import { Token } from '../types/index' +import { Token } from '../../types/index' import * as CHAINS from '../chains/master' import * as all from './bridgeable' import * as allPool from './poolMaster' @@ -51,7 +51,7 @@ export const findChainIdsWithPausedToken = (routeSymbol: string) => { PAUSED_TOKENS_BY_CHAIN, (result, tokens, chainId) => { if (_.includes(tokens, routeSymbol)) { - result.push(chainId) + result.push(chainId as never) } return result }, diff --git a/packages/synapse-constants/constants/tokens/master.ts b/packages/synapse-constants/src/constants/tokens/master.ts similarity index 100% rename from packages/synapse-constants/constants/tokens/master.ts rename to packages/synapse-constants/src/constants/tokens/master.ts diff --git a/packages/synapse-constants/constants/tokens/poolMaster.ts b/packages/synapse-constants/src/constants/tokens/poolMaster.ts similarity index 90% rename from packages/synapse-constants/constants/tokens/poolMaster.ts rename to packages/synapse-constants/src/constants/tokens/poolMaster.ts index 972d8711e6..d096ed5fba 100644 --- a/packages/synapse-constants/constants/tokens/poolMaster.ts +++ b/packages/synapse-constants/src/constants/tokens/poolMaster.ts @@ -1,6 +1,4 @@ -import synapseLogo from '../assets/icons/syn.svg' -import { Token } from '../types' - +import { Token } from '../../types' import * as CHAINS from '../chains/master' import { BUSD, @@ -45,7 +43,7 @@ export const ETH_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD', name: 'Synapse nUSD LP Token Ethereum', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Ethereum Stableswap Pool', routerIndex: 'eth3pool', poolId: 420, @@ -72,7 +70,7 @@ export const BSC_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'BSC Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'bscnusd', poolId: 1, @@ -99,7 +97,7 @@ export const OPTIMISM_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Optimism ', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Optimism Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'optimismnusd', poolId: 1, @@ -124,7 +122,7 @@ export const CRONOS_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Cronos ', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Cronos Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'cronosnusd', poolId: 0, @@ -149,7 +147,7 @@ export const POLYGON_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Polygon ', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Polygon Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'polygonnusd', poolId: 1, @@ -176,7 +174,7 @@ export const AVALANCHE_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Avalanche', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Avalanche Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'avalanchenusd', // poolId: 3, @@ -202,7 +200,7 @@ export const LEGACY_AVALANCHE_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Avalanche', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Legacy Avalanche Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'legacyavalanchenusd', poolId: 1, @@ -225,7 +223,7 @@ export const ARBITRUM_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Arbitrum', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Legacy Arbitrum Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'arbitrumnusd', poolId: 2, @@ -252,7 +250,7 @@ export const FANTOM_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Fantom', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Legacy Fantom Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'fantomnusd', poolId: 1, @@ -279,7 +277,7 @@ export const HARMONY_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Harmony', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Harmony Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'harmonynusd', poolId: 1, @@ -306,7 +304,7 @@ export const BOBA_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Boba', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Boba Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'bobanusd', poolId: 1, @@ -333,7 +331,7 @@ export const AURORA_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Aurora', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Legacy Aurora Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'auroranusd', poolId: 0, @@ -358,7 +356,7 @@ export const AURORA_TS_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Trisolaris nUSD LP Token Aurora', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Aurora Trisolaris Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'auroratrisolarisnusd', poolId: 0, @@ -384,7 +382,7 @@ export const ARBITRUM_3POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSDLP', name: 'Synapse nUSD LP Token Arbitrum', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Arbitrum 3Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'arbitrum3pool', poolId: 3, @@ -410,7 +408,7 @@ export const FANTOM_3POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSDLP', name: 'Synapse nUSD LP Token Fantom', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Fantom 3Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'fantom3pool', poolId: 3, @@ -436,7 +434,7 @@ export const METIS_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSDLP', name: 'Synapse nUSD LP Token Metis', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Metis Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'metis2pool', poolId: 0, @@ -462,7 +460,7 @@ export const CANTO_POOL_SWAP_TOKEN = new Token({ decimals: 18, name: 'Synapse nUSD LP Token Canto', symbol: 'nUSDLP', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Canto NOTE Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'canto2pool', poolId: 0, @@ -486,7 +484,7 @@ export const CANTO_POOL_SWAP_TOKEN = new Token({ // decimals: 18, // name: 'Synapse nUSD LP Token Canto', // symbol: 'nUSD-LP', -// logo: synapseLogo, +// logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', // poolName: 'Canto USDC Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL // routerIndex: 'cantousdcpool', // poolId: 2, @@ -509,7 +507,7 @@ export const CANTO_WRAPPER_POOL_SWAP_TOKEN = new Token({ decimals: 18, name: 'Synapse nUSD LP Token Canto', symbol: 'Wrapper', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Canto Wrapper Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'cantowrapper', poolId: 420, @@ -535,7 +533,7 @@ export const KLAYTN_ORBIT_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'oUSDT-LP', name: 'Synapse Orbit UST LP Token Klaytn', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Klaytn Synapse & Orbit USDT Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'klaytn2pool', poolId: 0, @@ -560,7 +558,7 @@ export const HARMONY_AVAX_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'AVAXLP', name: 'AVAX LP Token Harmony ', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Harmony AVAX Swap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'avax2pool', poolId: 0, @@ -587,7 +585,7 @@ export const ARBITRUM_ETH_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nETH-LP', // make sure this gets update to match conytract name: 'Synapse Eth LP Token Arbitrum', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Arbitrum ETH Pool', routerIndex: 'arbitrumethpool', poolId: 0, @@ -618,7 +616,7 @@ export const OPTIMISM_ETH_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nETH-LP', // make sure this gets update to match conytract name: 'Synapse Eth LP Token Optimism', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Optimism ETH Pool', routerIndex: 'optimismethpool', poolId: 0, @@ -649,7 +647,7 @@ export const BOBA_ETH_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nETH-LP', // make sure this gets update to match conytract name: 'Synapse Eth LP Token Boba', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Boba ETH Pool', routerIndex: 'bobaethpool', poolId: 2, @@ -680,7 +678,7 @@ export const AVALANCHE_AVETH_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nETH-LP', // make sure this gets update to match conytract name: 'Synapse Eth LP Token Avalanche', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Avalanche ETH Pool', routerIndex: 'avalancheethpool', poolId: 2, @@ -715,7 +713,7 @@ export const HARMONY_ONEETH_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nETH-LP', name: 'Synapse 1ETH LP Token Harmony', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Harmony 1ETH Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'harmonyethpool', poolId: 2, @@ -741,7 +739,7 @@ export const FANTOM_WETH_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nETH-LP', name: 'Synapse ETH LP Token Fantom', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Fantom ETH Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'fantomethpool', poolId: 2, @@ -767,7 +765,7 @@ export const METIS_WETH_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nETH-LP', name: 'Synapse ETH LP Token Metis', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Metis ETH Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'metisethpool', poolId: 1, @@ -794,7 +792,7 @@ export const CANTO_WETH_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nETH-LP', name: 'Synapse ETH LP Token Canto', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Canto ETH Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'cantoethpool', poolId: 1, @@ -820,7 +818,7 @@ export const HARMONY_JEWEL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'JEWELP', name: 'Jewel LP Token Harmony ', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Harmony Jewel Swap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'jewel2pool', poolId: 0, @@ -845,7 +843,7 @@ export const BASE_ETH_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nETH-LP', name: 'Synapse Eth LP Token Base', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Base ETH Pool', routerIndex: 'baseethpool', poolId: 0, @@ -878,7 +876,7 @@ export const METIS_WETH_SWAP_TOKEN_MIGRATED = new Token({ decimals: 18, symbol: 'nETH-LP', name: 'Synapse ETH LP Token Metis', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Metis ETH Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'metisethpool-migrated', poolId: 1, @@ -904,7 +902,7 @@ export const METIS_POOL_SWAP_TOKEN_MIGRATED = new Token({ decimals: 18, symbol: 'nUSDLP', name: 'Synapse nUSD LP Token Metis', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Metis Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'metis2pool-migrated', poolId: 0, diff --git a/packages/synapse-constants/constants/tokens/sushiMaster.ts b/packages/synapse-constants/src/constants/tokens/sushiMaster.ts similarity index 81% rename from packages/synapse-constants/constants/tokens/sushiMaster.ts rename to packages/synapse-constants/src/constants/tokens/sushiMaster.ts index ca302ef70f..347142245b 100644 --- a/packages/synapse-constants/constants/tokens/sushiMaster.ts +++ b/packages/synapse-constants/src/constants/tokens/sushiMaster.ts @@ -1,6 +1,4 @@ -import sushiLogo from '../assets/icons/sushi.svg' -import { Token } from '../types' - +import { Token } from '../../types' import * as CHAINS from '../chains/master' import { MINICHEF_ADDRESSES } from '../minichef' @@ -11,7 +9,7 @@ export const SYN_ETH_SUSHI_TOKEN = new Token({ decimals: 18, symbol: 'SYN/ETH-SLP', name: 'SYN/ETH Sushi LP', - logo: sushiLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'SYN/ETH Sushiswap LP', poolId: 0, poolType: 'EXTERNAL_LP', @@ -28,7 +26,7 @@ export const ETH_USDC_SUSHI_TOKEN = new Token({ decimals: 18, symbol: 'ETH/USDC-SLP', name: 'ETH/USDC Sushi LP', - logo: sushiLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/eth.b3692688.svg', poolName: 'ETH/USDC Sushiswap LP', poolId: 666, poolType: 'EXTERNAL_LP', diff --git a/packages/synapse-constants/constants/tokens/swapMaster.ts b/packages/synapse-constants/src/constants/tokens/swapMaster.ts similarity index 84% rename from packages/synapse-constants/constants/tokens/swapMaster.ts rename to packages/synapse-constants/src/constants/tokens/swapMaster.ts index e5a94bc9dd..dea9ff5a28 100644 --- a/packages/synapse-constants/constants/tokens/swapMaster.ts +++ b/packages/synapse-constants/src/constants/tokens/swapMaster.ts @@ -1,6 +1,4 @@ -import { Token } from '../types' -import usdtLogo from '../assets/icons/usdt.svg' -import usdcLogo from '../assets/icons/usdc.svg' +import { Token } from '../../types' import * as CHAINS from '../chains/master' export const SwapUSDC = new Token({ @@ -13,7 +11,7 @@ export const SwapUSDC = new Token({ }, symbol: 'USDC', name: 'USD Circle', - logo: usdcLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdc.d5dcb030.svg', description: ` USD Coin (known by its ticker USDC) is a stablecoin that is pegged to the U.S. dollar on a 1:1 basis. Every unit of this cryptocurrency in circulation @@ -35,7 +33,7 @@ export const SwapUSDT = new Token({ }, symbol: 'USDT', name: 'USD Tether', - logo: usdtLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdt.3c9cd2f8.svg', color: 'lime', description: ` USDT mirrors the price of the U.S. dollar, issued by a Hong Kong-based company Tether. diff --git a/packages/synapse-constants/custom.d.ts b/packages/synapse-constants/src/custom.d.ts similarity index 100% rename from packages/synapse-constants/custom.d.ts rename to packages/synapse-constants/src/custom.d.ts diff --git a/packages/synapse-constants/src/index.ts b/packages/synapse-constants/src/index.ts new file mode 100644 index 0000000000..24f061e0e5 --- /dev/null +++ b/packages/synapse-constants/src/index.ts @@ -0,0 +1,6 @@ +export * from './constants/tokens/index' +export * as CHAINS from './constants/chains/index' +export * from './types/index' + +export { MINICHEF_ADDRESSES } from './constants/minichef' +export { BRIDGE_MAP } from './constants/bridgeMap' diff --git a/packages/synapse-constants/scripts/abi/IDefaultPool.json b/packages/synapse-constants/src/scripts/abi/IDefaultPool.json similarity index 100% rename from packages/synapse-constants/scripts/abi/IDefaultPool.json rename to packages/synapse-constants/src/scripts/abi/IDefaultPool.json diff --git a/packages/synapse-constants/scripts/abi/IERC20Metadata.json b/packages/synapse-constants/src/scripts/abi/IERC20Metadata.json similarity index 100% rename from packages/synapse-constants/scripts/abi/IERC20Metadata.json rename to packages/synapse-constants/src/scripts/abi/IERC20Metadata.json diff --git a/packages/synapse-constants/scripts/abi/SwapQuoter.json b/packages/synapse-constants/src/scripts/abi/SwapQuoter.json similarity index 100% rename from packages/synapse-constants/scripts/abi/SwapQuoter.json rename to packages/synapse-constants/src/scripts/abi/SwapQuoter.json diff --git a/packages/synapse-constants/scripts/abi/SynapseCCTP.json b/packages/synapse-constants/src/scripts/abi/SynapseCCTP.json similarity index 100% rename from packages/synapse-constants/scripts/abi/SynapseCCTP.json rename to packages/synapse-constants/src/scripts/abi/SynapseCCTP.json diff --git a/packages/synapse-constants/scripts/abi/SynapseCCTPRouter.json b/packages/synapse-constants/src/scripts/abi/SynapseCCTPRouter.json similarity index 100% rename from packages/synapse-constants/scripts/abi/SynapseCCTPRouter.json rename to packages/synapse-constants/src/scripts/abi/SynapseCCTPRouter.json diff --git a/packages/synapse-constants/scripts/abi/SynapseRouter.json b/packages/synapse-constants/src/scripts/abi/SynapseRouter.json similarity index 100% rename from packages/synapse-constants/scripts/abi/SynapseRouter.json rename to packages/synapse-constants/src/scripts/abi/SynapseRouter.json diff --git a/packages/synapse-constants/scripts/data/ignoredBridgeSymbols.json b/packages/synapse-constants/src/scripts/data/ignoredBridgeSymbols.json similarity index 100% rename from packages/synapse-constants/scripts/data/ignoredBridgeSymbols.json rename to packages/synapse-constants/src/scripts/data/ignoredBridgeSymbols.json diff --git a/packages/synapse-constants/scripts/data/legacyTokens.json b/packages/synapse-constants/src/scripts/data/legacyTokens.json similarity index 100% rename from packages/synapse-constants/scripts/data/legacyTokens.json rename to packages/synapse-constants/src/scripts/data/legacyTokens.json diff --git a/packages/synapse-constants/scripts/data/providers.json b/packages/synapse-constants/src/scripts/data/providers.json similarity index 100% rename from packages/synapse-constants/scripts/data/providers.json rename to packages/synapse-constants/src/scripts/data/providers.json diff --git a/packages/synapse-constants/scripts/data/symbolOverrides.json b/packages/synapse-constants/src/scripts/data/symbolOverrides.json similarity index 100% rename from packages/synapse-constants/scripts/data/symbolOverrides.json rename to packages/synapse-constants/src/scripts/data/symbolOverrides.json diff --git a/packages/synapse-constants/scripts/findMissing.js b/packages/synapse-constants/src/scripts/findMissing.cjs similarity index 100% rename from packages/synapse-constants/scripts/findMissing.js rename to packages/synapse-constants/src/scripts/findMissing.cjs diff --git a/packages/synapse-constants/scripts/generateMaps.js b/packages/synapse-constants/src/scripts/generateMaps.cjs similarity index 99% rename from packages/synapse-constants/scripts/generateMaps.js rename to packages/synapse-constants/src/scripts/generateMaps.cjs index f37ada1328..93e9f41b13 100644 --- a/packages/synapse-constants/scripts/generateMaps.js +++ b/packages/synapse-constants/src/scripts/generateMaps.cjs @@ -1,7 +1,7 @@ const { ethers } = require('ethers') -const { prettyPrintTS } = require('./utils/prettyPrintTs') -const { fetchRfqData } = require('./utils/fetchRfqData') +const { prettyPrintTS } = require('./utils/prettyPrintTs.cjs') +const { fetchRfqData } = require('./utils/fetchRfqData.cjs') // Provider URLs const providers = require('./data/providers.json') // List of ignored bridge symbols diff --git a/packages/synapse-constants/unsupportedTokens.txt b/packages/synapse-constants/src/scripts/output/unsupportedTokens.txt similarity index 100% rename from packages/synapse-constants/unsupportedTokens.txt rename to packages/synapse-constants/src/scripts/output/unsupportedTokens.txt diff --git a/packages/synapse-constants/scripts/utils/fetchRfqData.js b/packages/synapse-constants/src/scripts/utils/fetchRfqData.cjs similarity index 100% rename from packages/synapse-constants/scripts/utils/fetchRfqData.js rename to packages/synapse-constants/src/scripts/utils/fetchRfqData.cjs diff --git a/packages/synapse-constants/scripts/utils/prettyPrintTs.js b/packages/synapse-constants/src/scripts/utils/prettyPrintTs.cjs similarity index 100% rename from packages/synapse-constants/scripts/utils/prettyPrintTs.js rename to packages/synapse-constants/src/scripts/utils/prettyPrintTs.cjs diff --git a/packages/synapse-constants/constants/types/index.tsx b/packages/synapse-constants/src/types/index.ts similarity index 92% rename from packages/synapse-constants/constants/types/index.tsx rename to packages/synapse-constants/src/types/index.ts index 45569b975c..f30e6c12f5 100644 --- a/packages/synapse-constants/constants/types/index.tsx +++ b/packages/synapse-constants/src/types/index.ts @@ -1,6 +1,7 @@ import { BigNumber } from '@ethersproject/bignumber' -import * as CHAINS from '../chains/master' -import { getAddress } from '@ethersproject/address' + +import { makeMultiChainObj } from '../utils/makeMultiChainObj' +import { validateAddresses } from '../utils/validateAddresses' export type Chain = { id: number @@ -19,6 +20,7 @@ export type Chain = { priorityRank?: number color?: string } + export type PoolToken = { symbol: string percent: string @@ -28,6 +30,7 @@ export type PoolToken = { isLP: boolean rawBalance: bigint } + export type Query = [string, string, BigNumber, BigNumber, string] & { swapAdapter: string tokenOut: string @@ -35,12 +38,14 @@ export type Query = [string, string, BigNumber, BigNumber, string] & { deadline: BigNumber rawParams: string } + export type PoolUserData = { name: string tokens: PoolToken[] lpTokenBalance: bigint nativeTokens?: any } + export type PoolData = { name: string tokens: PoolToken[] @@ -54,26 +59,31 @@ export type PoolData = { interface TokensByChain { [cID: string]: Token[] } + export type PoolCardInfo = { index: number label: string poolsByChain: TokensByChain } + export enum WalletId { MetaMask = 'metaMask', WalletConnect = 'walletConnect', CoinbaseWallet = 'coinbaseWallet', } + export interface IconProps { walletId?: string className?: string } + export type PoolTokenObject = { token: Token balance: string rawBalance: bigint isLP: boolean } + /** * Represents an ERC20-like token with a unique address, chainId, and some metadata. */ @@ -277,27 +287,3 @@ export class Token { this.routeSymbol = routeSymbol } } - -const makeMultiChainObj = (valOrObj: any) => { - if (typeof valOrObj === 'object') { - return valOrObj - } else { - const obj: { [key: number]: any } = {} - for (const chain of Object.values(CHAINS)) { - obj[chain.id] = valOrObj - } - return obj - } -} - -const validateAddresses = (addresses: { - [x: number]: string -}): { [x: number]: string } => { - const reformatted: { [x: number]: string } = {} - for (const chainId in addresses) { - reformatted[chainId] = addresses[chainId] - ? getAddress(addresses[chainId]) - : '' - } - return reformatted -} diff --git a/packages/synapse-constants/src/utils/makeMultiChainObj.ts b/packages/synapse-constants/src/utils/makeMultiChainObj.ts new file mode 100644 index 0000000000..18a66dcb2b --- /dev/null +++ b/packages/synapse-constants/src/utils/makeMultiChainObj.ts @@ -0,0 +1,13 @@ +import * as CHAINS from '../constants/chains/master' + +export const makeMultiChainObj = (valOrObj: any) => { + if (typeof valOrObj === 'object') { + return valOrObj + } else { + const obj: { [key: number]: any } = {} + for (const chain of Object.values(CHAINS)) { + obj[chain.id] = valOrObj + } + return obj + } +} diff --git a/packages/synapse-constants/src/utils/validateAddresses.ts b/packages/synapse-constants/src/utils/validateAddresses.ts new file mode 100644 index 0000000000..517cd7a7e4 --- /dev/null +++ b/packages/synapse-constants/src/utils/validateAddresses.ts @@ -0,0 +1,13 @@ +import { getAddress } from '@ethersproject/address' + +export const validateAddresses = (addresses: { + [x: number]: string +}): { [x: number]: string } => { + const reformatted: { [x: number]: string } = {} + for (const chainId in addresses) { + reformatted[chainId] = addresses[chainId] + ? getAddress(addresses[chainId]) + : '' + } + return reformatted +} diff --git a/packages/synapse-constants/tsconfig.json b/packages/synapse-constants/tsconfig.json index dc0b7533d7..5105bbc9a4 100644 --- a/packages/synapse-constants/tsconfig.json +++ b/packages/synapse-constants/tsconfig.json @@ -1,24 +1,15 @@ { "compilerOptions": { - "outDir": "./dist", - "allowJs": true, - "rootDir": "./", + "module": "ESNext", "target": "ES2019", - "module": "CommonJS", "moduleResolution": "node", + "declaration": true, + "declarationDir": "./dist/types", + "outDir": "./dist", + "strict": true, "esModuleInterop": true, - "declaration": false, - "jsx": "react", - "skipLibCheck": true + "resolveJsonModule": true }, - "include": [ - "./constants/**/*", - "./custom.d.ts", - "./index.ts", - "./index.d.ts" - ], - "exclude": [ - "node_modules", - "**/*.spec.ts" - ] -} \ No newline at end of file + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/synapse-constants/webpack.config.js b/packages/synapse-constants/webpack.config.js deleted file mode 100644 index 6ba4758070..0000000000 --- a/packages/synapse-constants/webpack.config.js +++ /dev/null @@ -1,86 +0,0 @@ -const path = require('path') - -const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin') -const { codecovWebpackPlugin } = require('@codecov/webpack-plugin') - -module.exports = { - mode: 'production', - - entry: './dist/', - - output: { - path: path.resolve(__dirname, 'dist'), - filename: 'bundle.js', - }, - plugins: [ - // Put the Codecov Webpack plugin after all other plugins - codecovWebpackPlugin({ - enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, - bundleName: 'synapse-constants', - uploadToken: process.env.CODECOV_TOKEN, - uploadOverrides: { - sha: process.env.GH_COMMIT_SHA, - }, - }), - ], - - resolve: { - extensions: ['.ts', '.tsx', '.js'], - modules: [path.resolve(__dirname, '../../node_modules')], - }, - - module: { - rules: [ - { - test: /\.ts|tsx$/, - loader: 'ts-loader', - exclude: /node_modules/, - }, - { - test: /\.svg$/, - loader: 'svg-inline-loader', - }, - { - test: /\.(png|jpg|jpeg|gif)$/, - type: 'asset', - }, - ], - }, - optimization: { - minimizer: [ - '...', - new ImageMinimizerPlugin({ - minimizer: { - implementation: ImageMinimizerPlugin.imageminMinify, - options: { - // Lossless optimization with custom option - // Feel free to experiment with options for better result for you - plugins: [ - ['optipng', { optimizationLevel: 5 }], - // Svgo configuration here https://github.com/svg/svgo#configuration - [ - 'svgo', - { - plugins: [ - { - name: 'preset-default', - params: { - overrides: { - removeViewBox: false, - inlineStyles: { - onlyMatchedOnce: false, - }, - }, - }, - }, - ], - multipass: true, - }, - ], - ], - }, - }, - }), - ], - }, -} diff --git a/yarn.lock b/yarn.lock index 826b7331b8..f4014e15f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,32 @@ # yarn lockfile v1 +"@actions/core@^1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.10.1.tgz#61108e7ac40acae95ee36da074fa5850ca4ced8a" + integrity sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g== + dependencies: + "@actions/http-client" "^2.0.1" + uuid "^8.3.2" + +"@actions/github@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@actions/github/-/github-6.0.0.tgz#65883433f9d81521b782a64cc1fd45eef2191ea7" + integrity sha512-alScpSVnYmjNEXboZjarjukQEzgCRmjMv6Xj47fsdnqGS73bjJNDpiiXmp8jr0UZLdUB6d9jW63IcmddUP+l0g== + dependencies: + "@actions/http-client" "^2.2.0" + "@octokit/core" "^5.0.1" + "@octokit/plugin-paginate-rest" "^9.0.0" + "@octokit/plugin-rest-endpoint-methods" "^10.0.0" + +"@actions/http-client@^2.0.1", "@actions/http-client@^2.2.0": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-2.2.3.tgz#31fc0b25c0e665754ed39a9f19a8611fc6dab674" + integrity sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA== + dependencies: + tunnel "^0.0.6" + undici "^5.25.4" + "@adobe/css-tools@^4.0.1": version "4.4.0" resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.0.tgz#728c484f4e10df03d5a3acd0d8adcbbebff8ad63" @@ -1827,6 +1853,18 @@ unplugin "^1.10.1" zod "^3.22.4" +"@codecov/bundler-plugin-core@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@codecov/bundler-plugin-core/-/bundler-plugin-core-1.2.0.tgz#4a896dd3bd9f9f98a60519aadcf8e3daff37ae5d" + integrity sha512-ublUP5V0tW6oDnaJ1UBWvEmVAkvMmPNEwWkpF+WwJSCBWNLvWrkSwG84S3Gt5Xbnh17xEyAxXBmNzF+mXVXBgw== + dependencies: + "@actions/core" "^1.10.1" + "@actions/github" "^6.0.0" + chalk "4.1.2" + semver "^7.5.4" + unplugin "^1.10.1" + zod "^3.22.4" + "@codecov/rollup-plugin@^0.0.1-beta.10": version "0.0.1-beta.12" resolved "https://registry.yarnpkg.com/@codecov/rollup-plugin/-/rollup-plugin-0.0.1-beta.12.tgz#addf8e7689a7a387817911ac3c0ad4c26980bd7b" @@ -1835,6 +1873,14 @@ "@codecov/bundler-plugin-core" "^0.0.1-beta.12" unplugin "^1.10.1" +"@codecov/rollup-plugin@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@codecov/rollup-plugin/-/rollup-plugin-1.2.0.tgz#407f757010b4f3fab3f123a5f598b20ec837426f" + integrity sha512-4QgGHwvZ2mL4XOLf4jVz1bG9vyTT7+vCQ6d4Qi1+ttboMjNabmw5XO4WfSOwj+6kwuid8VQJmEF8/hJ6aZuc3w== + dependencies: + "@codecov/bundler-plugin-core" "^1.2.0" + unplugin "^1.10.1" + "@codecov/webpack-plugin@^0.0.1-beta.10": version "0.0.1-beta.12" resolved "https://registry.yarnpkg.com/@codecov/webpack-plugin/-/webpack-plugin-0.0.1-beta.12.tgz#ea067e60b31f7168d99b26fae5c5d1ac80669d64" @@ -1972,7 +2018,7 @@ "@devtools-ds/themes" "^1.2.1" clsx "1.1.0" -"@discoveryjs/json-ext@0.5.7", "@discoveryjs/json-ext@^0.5.0", "@discoveryjs/json-ext@^0.5.3": +"@discoveryjs/json-ext@0.5.7", "@discoveryjs/json-ext@^0.5.3": version "0.5.7" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== @@ -6411,6 +6457,11 @@ dependencies: "@octokit/types" "^6.0.3" +"@octokit/auth-token@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-4.0.0.tgz#40d203ea827b9f17f42a29c6afb93b7745ef80c7" + integrity sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA== + "@octokit/core@^3.5.1": version "3.6.0" resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.6.0.tgz#3376cb9f3008d9b3d110370d90e0a1fcd5fe6085" @@ -6424,6 +6475,19 @@ before-after-hook "^2.2.0" universal-user-agent "^6.0.0" +"@octokit/core@^5.0.1": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-5.2.0.tgz#ddbeaefc6b44a39834e1bb2e58a49a117672a7ea" + integrity sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg== + dependencies: + "@octokit/auth-token" "^4.0.0" + "@octokit/graphql" "^7.1.0" + "@octokit/request" "^8.3.1" + "@octokit/request-error" "^5.1.0" + "@octokit/types" "^13.0.0" + before-after-hook "^2.2.0" + universal-user-agent "^6.0.0" + "@octokit/endpoint@^6.0.1": version "6.0.12" resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" @@ -6433,6 +6497,14 @@ is-plain-object "^5.0.0" universal-user-agent "^6.0.0" +"@octokit/endpoint@^9.0.1": + version "9.0.5" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-9.0.5.tgz#e6c0ee684e307614c02fc6ac12274c50da465c44" + integrity sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw== + dependencies: + "@octokit/types" "^13.1.0" + universal-user-agent "^6.0.0" + "@octokit/graphql@^4.5.8": version "4.8.0" resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3" @@ -6442,11 +6514,30 @@ "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" +"@octokit/graphql@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-7.1.0.tgz#9bc1c5de92f026648131f04101cab949eeffe4e0" + integrity sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ== + dependencies: + "@octokit/request" "^8.3.0" + "@octokit/types" "^13.0.0" + universal-user-agent "^6.0.0" + "@octokit/openapi-types@^12.11.0": version "12.11.0" resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0" integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ== +"@octokit/openapi-types@^20.0.0": + version "20.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-20.0.0.tgz#9ec2daa0090eeb865ee147636e0c00f73790c6e5" + integrity sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA== + +"@octokit/openapi-types@^22.2.0": + version "22.2.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-22.2.0.tgz#75aa7dcd440821d99def6a60b5f014207ae4968e" + integrity sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg== + "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" @@ -6459,11 +6550,25 @@ dependencies: "@octokit/types" "^6.40.0" +"@octokit/plugin-paginate-rest@^9.0.0": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.1.tgz#2e2a2f0f52c9a4b1da1a3aa17dabe3c459b9e401" + integrity sha512-wfGhE/TAkXZRLjksFXuDZdmGnJQHvtU/joFQdweXUgzo1XwvBCD4o4+75NtFfjfLK5IwLf9vHTfSiU3sLRYpRw== + dependencies: + "@octokit/types" "^12.6.0" + "@octokit/plugin-request-log@^1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== +"@octokit/plugin-rest-endpoint-methods@^10.0.0": + version "10.4.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz#41ba478a558b9f554793075b2e20cd2ef973be17" + integrity sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg== + dependencies: + "@octokit/types" "^12.6.0" + "@octokit/plugin-rest-endpoint-methods@^5.12.0": version "5.16.2" resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz#7ee8bf586df97dd6868cf68f641354e908c25342" @@ -6481,6 +6586,15 @@ deprecation "^2.0.0" once "^1.4.0" +"@octokit/request-error@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-5.1.0.tgz#ee4138538d08c81a60be3f320cd71063064a3b30" + integrity sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q== + dependencies: + "@octokit/types" "^13.1.0" + deprecation "^2.0.0" + once "^1.4.0" + "@octokit/request@^5.6.0", "@octokit/request@^5.6.3": version "5.6.3" resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" @@ -6493,6 +6607,16 @@ node-fetch "^2.6.7" universal-user-agent "^6.0.0" +"@octokit/request@^8.3.0", "@octokit/request@^8.3.1": + version "8.4.0" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-8.4.0.tgz#7f4b7b1daa3d1f48c0977ad8fffa2c18adef8974" + integrity sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw== + dependencies: + "@octokit/endpoint" "^9.0.1" + "@octokit/request-error" "^5.1.0" + "@octokit/types" "^13.1.0" + universal-user-agent "^6.0.0" + "@octokit/rest@^18.1.0": version "18.12.0" resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" @@ -6503,6 +6627,20 @@ "@octokit/plugin-request-log" "^1.0.4" "@octokit/plugin-rest-endpoint-methods" "^5.12.0" +"@octokit/types@^12.6.0": + version "12.6.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-12.6.0.tgz#8100fb9eeedfe083aae66473bd97b15b62aedcb2" + integrity sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw== + dependencies: + "@octokit/openapi-types" "^20.0.0" + +"@octokit/types@^13.0.0", "@octokit/types@^13.1.0": + version "13.5.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-13.5.0.tgz#4796e56b7b267ebc7c921dcec262b3d5bfb18883" + integrity sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ== + dependencies: + "@octokit/openapi-types" "^22.2.0" + "@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.39.0", "@octokit/types@^6.40.0": version "6.41.0" resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04" @@ -6891,6 +7029,19 @@ is-reference "1.2.1" magic-string "^0.30.3" +"@rollup/plugin-commonjs@^28.0.0": + version "28.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.0.tgz#44b5e49cb5d5e6233f1e4996013a8649fdbb9557" + integrity sha512-BJcu+a+Mpq476DMXG+hevgPSl56bkUoi88dKT8t3RyUp8kGuOh+2bU8Gs7zXDlu+fyZggnJ+iOBGrb/O1SorYg== + dependencies: + "@rollup/pluginutils" "^5.0.1" + commondir "^1.0.1" + estree-walker "^2.0.2" + fdir "^6.1.1" + is-reference "1.2.1" + magic-string "^0.30.3" + picomatch "^2.3.1" + "@rollup/plugin-json@^4.0.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3" @@ -6898,7 +7049,7 @@ dependencies: "@rollup/pluginutils" "^3.0.8" -"@rollup/plugin-json@^6.0.1": +"@rollup/plugin-json@^6.0.1", "@rollup/plugin-json@^6.1.0": version "6.1.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-6.1.0.tgz#fbe784e29682e9bb6dee28ea75a1a83702e7b805" integrity sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA== @@ -6917,6 +7068,17 @@ is-module "^1.0.0" resolve "^1.22.1" +"@rollup/plugin-node-resolve@^15.3.0": + version "15.3.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz#efbb35515c9672e541c08d59caba2eff492a55d5" + integrity sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag== + dependencies: + "@rollup/pluginutils" "^5.0.1" + "@types/resolve" "1.20.2" + deepmerge "^4.2.2" + is-module "^1.0.0" + resolve "^1.22.1" + "@rollup/plugin-node-resolve@^9.0.0": version "9.0.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-9.0.0.tgz#39bd0034ce9126b39c1699695f440b4b7d2b62e6" @@ -6954,6 +7116,15 @@ "@rollup/pluginutils" "^5.1.0" resolve "^1.22.1" +"@rollup/plugin-url@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@rollup/plugin-url/-/plugin-url-8.0.2.tgz#aab4e209e9e012f65582bd99eb80b3bbdfe15afb" + integrity sha512-5yW2LP5NBEgkvIRSSEdJkmxe5cUNZKG3eenKtfJvSkxVm/xTTu7w+ayBtNwhozl1ZnTUCU0xFaRQR+cBl2H7TQ== + dependencies: + "@rollup/pluginutils" "^5.0.1" + make-dir "^3.1.0" + mime "^3.0.0" + "@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.0.9", "@rollup/pluginutils@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" @@ -6963,7 +7134,7 @@ estree-walker "^1.0.1" picomatch "^2.2.2" -"@rollup/pluginutils@^4.0.0": +"@rollup/pluginutils@^4.0.0", "@rollup/pluginutils@^4.1.2": version "4.2.1" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d" integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ== @@ -6985,81 +7156,161 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.3.tgz#155c7d82c1b36c3ad84d9adf9b3cd520cba81a0f" integrity sha512-MmKSfaB9GX+zXl6E8z4koOr/xU63AMVleLEa64v7R0QF/ZloMs5vcD1sHgM64GXXS1csaJutG+ddtzcueI/BLg== +"@rollup/rollup-android-arm-eabi@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz#8b613b9725e8f9479d142970b106b6ae878610d5" + integrity sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w== + "@rollup/rollup-android-arm64@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.3.tgz#b94b6fa002bd94a9cbd8f9e47e23b25e5bd113ba" integrity sha512-zrt8ecH07PE3sB4jPOggweBjJMzI1JG5xI2DIsUbkA+7K+Gkjys6eV7i9pOenNSDJH3eOr/jLb/PzqtmdwDq5g== +"@rollup/rollup-android-arm64@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz#654ca1049189132ff602bfcf8df14c18da1f15fb" + integrity sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA== + "@rollup/rollup-darwin-arm64@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.3.tgz#0934126cf9cbeadfe0eb7471ab5d1517e8cd8dcc" integrity sha512-P0UxIOrKNBFTQaXTxOH4RxuEBVCgEA5UTNV6Yz7z9QHnUJ7eLX9reOd/NYMO3+XZO2cco19mXTxDMXxit4R/eQ== +"@rollup/rollup-darwin-arm64@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz#6d241d099d1518ef0c2205d96b3fa52e0fe1954b" + integrity sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q== + "@rollup/rollup-darwin-x64@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.3.tgz#0ce8e1e0f349778938c7c90e4bdc730640e0a13e" integrity sha512-L1M0vKGO5ASKntqtsFEjTq/fD91vAqnzeaF6sfNAy55aD+Hi2pBI5DKwCO+UNDQHWsDViJLqshxOahXyLSh3EA== +"@rollup/rollup-darwin-x64@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz#42bd19d292a57ee11734c980c4650de26b457791" + integrity sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw== + "@rollup/rollup-linux-arm-gnueabihf@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.3.tgz#5669d34775ad5d71e4f29ade99d0ff4df523afb6" integrity sha512-btVgIsCjuYFKUjopPoWiDqmoUXQDiW2A4C3Mtmp5vACm7/GnyuprqIDPNczeyR5W8rTXEbkmrJux7cJmD99D2g== +"@rollup/rollup-linux-arm-gnueabihf@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz#f23555ee3d8fe941c5c5fd458cd22b65eb1c2232" + integrity sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ== + "@rollup/rollup-linux-arm-musleabihf@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.3.tgz#f6d1a0e1da4061370cb2f4244fbdd727c806dd88" integrity sha512-zmjbSphplZlau6ZTkxd3+NMtE4UKVy7U4aVFMmHcgO5CUbw17ZP6QCgyxhzGaU/wFFdTfiojjbLG3/0p9HhAqA== +"@rollup/rollup-linux-arm-musleabihf@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz#f3bbd1ae2420f5539d40ac1fde2b38da67779baa" + integrity sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg== + "@rollup/rollup-linux-arm64-gnu@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.3.tgz#ed96a05e99743dee4d23cc4913fc6e01a0089c88" integrity sha512-nSZfcZtAnQPRZmUkUQwZq2OjQciR6tEoJaZVFvLHsj0MF6QhNMg0fQ6mUOsiCUpTqxTx0/O6gX0V/nYc7LrgPw== +"@rollup/rollup-linux-arm64-gnu@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz#7abe900120113e08a1f90afb84c7c28774054d15" + integrity sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw== + "@rollup/rollup-linux-arm64-musl@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.3.tgz#057ea26eaa7e537a06ded617d23d57eab3cecb58" integrity sha512-MnvSPGO8KJXIMGlQDYfvYS3IosFN2rKsvxRpPO2l2cum+Z3exiExLwVU+GExL96pn8IP+GdH8Tz70EpBhO0sIQ== +"@rollup/rollup-linux-arm64-musl@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz#9e655285c8175cd44f57d6a1e8e5dedfbba1d820" + integrity sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA== + "@rollup/rollup-linux-powerpc64le-gnu@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.3.tgz#6e6e1f9404c9bf3fbd7d51cd11cd288a9a2843aa" integrity sha512-+W+p/9QNDr2vE2AXU0qIy0qQE75E8RTwTwgqS2G5CRQ11vzq0tbnfBd6brWhS9bCRjAjepJe2fvvkvS3dno+iw== +"@rollup/rollup-linux-powerpc64le-gnu@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz#9a79ae6c9e9d8fe83d49e2712ecf4302db5bef5e" + integrity sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg== + "@rollup/rollup-linux-riscv64-gnu@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.3.tgz#eef1536a53f6e6658a2a778130e6b1a4a41cb439" integrity sha512-yXH6K6KfqGXaxHrtr+Uoy+JpNlUlI46BKVyonGiaD74ravdnF9BUNC+vV+SIuB96hUMGShhKV693rF9QDfO6nQ== +"@rollup/rollup-linux-riscv64-gnu@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz#67ac70eca4ace8e2942fabca95164e8874ab8128" + integrity sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA== + "@rollup/rollup-linux-s390x-gnu@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.3.tgz#2b28fb89ca084efaf8086f435025d96b4a966957" integrity sha512-R8cwY9wcnApN/KDYWTH4gV/ypvy9yZUHlbJvfaiXSB48JO3KpwSpjOGqO4jnGkLDSk1hgjYkTbTt6Q7uvPf8eg== +"@rollup/rollup-linux-s390x-gnu@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz#9f883a7440f51a22ed7f99e1d070bd84ea5005fc" + integrity sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q== + "@rollup/rollup-linux-x64-gnu@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.3.tgz#5226cde6c6b495b04a3392c1d2c572844e42f06b" integrity sha512-kZPbX/NOPh0vhS5sI+dR8L1bU2cSO9FgxwM8r7wHzGydzfSjLRCFAT87GR5U9scj2rhzN3JPYVC7NoBbl4FZ0g== +"@rollup/rollup-linux-x64-gnu@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz#70116ae6c577fe367f58559e2cffb5641a1dd9d0" + integrity sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg== + "@rollup/rollup-linux-x64-musl@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.3.tgz#2c2412982e6c2a00a2ecac6d548ebb02f0aa6ca4" integrity sha512-S0Yq+xA1VEH66uiMNhijsWAafffydd2X5b77eLHfRmfLsRSpbiAWiRHV6DEpz6aOToPsgid7TI9rGd6zB1rhbg== +"@rollup/rollup-linux-x64-musl@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz#f473f88219feb07b0b98b53a7923be716d1d182f" + integrity sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g== + "@rollup/rollup-win32-arm64-msvc@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.3.tgz#fbb6ef5379199e2ec0103ef32877b0985c773a55" integrity sha512-9isNzeL34yquCPyerog+IMCNxKR8XYmGd0tHSV+OVx0TmE0aJOo9uw4fZfUuk2qxobP5sug6vNdZR6u7Mw7Q+Q== +"@rollup/rollup-win32-arm64-msvc@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz#4349482d17f5d1c58604d1c8900540d676f420e0" + integrity sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw== + "@rollup/rollup-win32-ia32-msvc@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.3.tgz#d50e2082e147e24d87fe34abbf6246525ec3845a" integrity sha512-nMIdKnfZfzn1Vsk+RuOvl43ONTZXoAPUUxgcU0tXooqg4YrAqzfKzVenqqk2g5efWh46/D28cKFrOzDSW28gTA== +"@rollup/rollup-win32-ia32-msvc@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz#a6fc39a15db618040ec3c2a24c1e26cb5f4d7422" + integrity sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g== + "@rollup/rollup-win32-x64-msvc@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.3.tgz#4115233aa1bd5a2060214f96d8511f6247093212" integrity sha512-fOvu7PCQjAj4eWDEuD8Xz5gpzFqXzGlxHZozHP4b9Jxv9APtdxL6STqztDzMLuRXEc4UpXGGhx029Xgm91QBeA== +"@rollup/rollup-win32-x64-msvc@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz#3dd5d53e900df2a40841882c02e56f866c04d202" + integrity sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q== + "@rtk-query/graphql-request-base-query@^2.2.0": version "2.3.1" resolved "https://registry.yarnpkg.com/@rtk-query/graphql-request-base-query/-/graphql-request-base-query-2.3.1.tgz#edfb2873705c4c6b05b171b6233c590de4572739" @@ -7324,11 +7575,6 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== -"@sindresorhus/is@^0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" - integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== - "@sindresorhus/is@^4.0.0", "@sindresorhus/is@^4.6.0": version "4.6.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" @@ -8853,11 +9099,6 @@ traverse "^0.6.7" unified "^9.2.2" -"@tokenizer/token@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276" - integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A== - "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -11006,21 +11247,6 @@ "@webassemblyjs/wast-parser" "1.9.0" "@xtuc/long" "4.2.2" -"@webpack-cli/configtest@^2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" - integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== - -"@webpack-cli/info@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" - integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== - -"@webpack-cli/serve@^2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" - integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== - "@whatwg-node/events@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@whatwg-node/events/-/events-0.0.3.tgz#13a65dd4f5893f55280f766e29ae48074927acad" @@ -11514,11 +11740,6 @@ ansi-sequence-parser@^1.1.0: resolved "https://registry.yarnpkg.com/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz#e0aa1cdcbc8f8bb0b5bca625aac41f5f056973cf" integrity sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg== -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA== - ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -11603,18 +11824,6 @@ aproba@^1.0.3, aproba@^1.1.1: resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== -arch@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" - integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== - -archive-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/archive-type/-/archive-type-4.0.0.tgz#f92e72233056dfc6969472749c267bdb046b1d70" - integrity sha512-zV4Ky0v1F8dBrdYElwTvQhweQ0P7Kwc1aluqJsYtOBP01jXcWCyW2IEfI1YiqsG+Iy7ZR+o5LF1N+PGECBxHWA== - dependencies: - file-type "^4.2.0" - archy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" @@ -12595,54 +12804,6 @@ bignumber@^1.1.0: resolved "https://registry.yarnpkg.com/bignumber/-/bignumber-1.1.0.tgz#e6ab0a743da5f3ea018e5c17597d121f7868c159" integrity sha512-EGqHCKkEAwVwufcEOCYhZQqdVH+7cNCyPZ9yxisYvSjHFB+d9YcGMvorsFpeN5IJpC+lC6K+FHhu8+S4MgJazw== -bin-build@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bin-build/-/bin-build-3.0.0.tgz#c5780a25a8a9f966d8244217e6c1f5082a143861" - integrity sha512-jcUOof71/TNAI2uM5uoUaDq2ePcVBQ3R/qhxAz1rX7UfvduAL/RXD3jXzvn8cVcDJdGVkiR1shal3OH0ImpuhA== - dependencies: - decompress "^4.0.0" - download "^6.2.2" - execa "^0.7.0" - p-map-series "^1.0.0" - tempfile "^2.0.0" - -bin-check@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bin-check/-/bin-check-4.1.0.tgz#fc495970bdc88bb1d5a35fc17e65c4a149fc4a49" - integrity sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA== - dependencies: - execa "^0.7.0" - executable "^4.1.0" - -bin-version-check@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/bin-version-check/-/bin-version-check-4.0.0.tgz#7d819c62496991f80d893e6e02a3032361608f71" - integrity sha512-sR631OrhC+1f8Cvs8WyVWOA33Y8tgwjETNPyyD/myRBXLkfS/vl74FmH/lFcRl9KY3zwGh7jFhvyk9vV3/3ilQ== - dependencies: - bin-version "^3.0.0" - semver "^5.6.0" - semver-truncate "^1.1.2" - -bin-version@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/bin-version/-/bin-version-3.1.0.tgz#5b09eb280752b1bd28f0c9db3f96f2f43b6c0839" - integrity sha512-Mkfm4iE1VFt4xd4vH+gx+0/71esbfus2LsnCGe8Pi4mndSPyT+NGES/Eg99jx8/lUGWfu3z2yuB/bt5UB+iVbQ== - dependencies: - execa "^1.0.0" - find-versions "^3.0.0" - -bin-wrapper@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bin-wrapper/-/bin-wrapper-4.1.0.tgz#99348f2cf85031e3ef7efce7e5300aeaae960605" - integrity sha512-hfRmo7hWIXPkbpi0ZltboCMVrU+0ClXR/JgbCKKjlDjQf6igXa7OwdqNcFWQZPZTgiY7ZpzE3+LjjkLiTN2T7Q== - dependencies: - bin-check "^4.1.0" - bin-version-check "^4.0.0" - download "^7.1.0" - import-lazy "^3.1.0" - os-filter-obj "^2.0.0" - pify "^4.0.1" - binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" @@ -12665,14 +12826,6 @@ bintrees@1.0.2: resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.2.tgz#49f896d6e858a4a499df85c38fb399b9aff840f8" integrity sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw== -bl@^1.0.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7" - integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww== - dependencies: - readable-stream "^2.3.5" - safe-buffer "^5.1.1" - bl@^4.0.3, bl@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" @@ -12978,29 +13131,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" -buffer-alloc-unsafe@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" - integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== - -buffer-alloc@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" - integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== - dependencies: - buffer-alloc-unsafe "^1.1.0" - buffer-fill "^1.0.0" - buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== -buffer-fill@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" - integrity sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ== - buffer-from@1.x, buffer-from@^1.0.0, buffer-from@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -13025,7 +13160,7 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" -buffer@^5.0.5, buffer@^5.2.1, buffer@^5.5.0, buffer@^5.6.0: +buffer@^5.0.5, buffer@^5.5.0, buffer@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -13211,19 +13346,6 @@ cacheable-request@^10.2.8: normalize-url "^8.0.0" responselike "^3.0.0" -cacheable-request@^2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" - integrity sha512-vag0O2LKZ/najSoUwDbVlnlCFvhBE/7mGTY2B5FgCBDcRD+oVV1HYTOwM6JZfMg/hIcM6IwnTZ1uQQL5/X3xIQ== - dependencies: - clone-response "1.0.2" - get-stream "3.0.0" - http-cache-semantics "3.8.1" - keyv "3.0.0" - lowercase-keys "1.0.0" - normalize-url "2.0.1" - responselike "1.0.2" - cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" @@ -13385,16 +13507,6 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== -caw@^2.0.0, caw@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/caw/-/caw-2.0.1.tgz#6c3ca071fc194720883c2dc5da9b074bfc7e9e95" - integrity sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA== - dependencies: - get-proxy "^2.0.0" - isurl "^1.0.0-alpha5" - tunnel-agent "^0.6.0" - url-to-options "^1.0.1" - "cbw-sdk@npm:@coinbase/wallet-sdk@3.9.3": version "3.9.3" resolved "https://registry.yarnpkg.com/@coinbase/wallet-sdk/-/wallet-sdk-3.9.3.tgz#daf10cb0c85d0363315b7270cb3f02bedc408aab" @@ -13441,17 +13553,6 @@ chalk@4.1.2, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1. ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -13919,13 +14020,6 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" -clone-response@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q== - dependencies: - mimic-response "^1.0.0" - clone-response@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" @@ -14084,7 +14178,7 @@ colorette@^1.2.0, colorette@^1.2.2: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== -colorette@^2.0.10, colorette@^2.0.14, colorette@^2.0.16: +colorette@^2.0.10, colorette@^2.0.16: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== @@ -14144,7 +14238,7 @@ command-line-usage@^6.1.0: table-layout "^1.0.2" typical "^5.2.0" -commander@2.20.3, commander@^2.19.0, commander@^2.20.0, commander@^2.8.1: +commander@2.20.3, commander@^2.19.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -14159,7 +14253,7 @@ commander@6.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.0.tgz#b990bfb8ac030aedc6d11bc04d1488ffef56db75" integrity sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q== -commander@^10.0.0, commander@^10.0.1: +commander@^10.0.0: version "10.0.1" resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== @@ -14387,11 +14481,6 @@ console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control- resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== -console-stream@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/console-stream/-/console-stream-0.1.1.tgz#a095fe07b20465955f2fafd28b5d72bccd949d44" - integrity sha512-QC/8l9e6ofi6nqZ5PawlDgzmMw3OxIXtvolBzap/F4UDBJlDaZRSNbL/lb41C29FcbSJncBFlJFj2WJoNyZRfQ== - constant-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" @@ -14411,7 +14500,7 @@ content-disposition@0.5.2: resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" integrity sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA== -content-disposition@0.5.4, content-disposition@^0.5.2: +content-disposition@0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== @@ -14603,7 +14692,7 @@ copy-webpack-plugin@^11.0.0: schema-utils "^4.0.0" serialize-javascript "^6.0.0" -copyfiles@^2.3.0, copyfiles@^2.4.1: +copyfiles@^2.3.0: version "2.4.1" resolved "https://registry.yarnpkg.com/copyfiles/-/copyfiles-2.4.1.tgz#d2dcff60aaad1015f09d0b66e7f0f1c5cd3c5da5" integrity sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg== @@ -14798,7 +14887,7 @@ cross-inspect@1.0.1: dependencies: tslib "^2.4.0" -cross-spawn@^5.0.1, cross-spawn@^5.1.0: +cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" integrity sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A== @@ -15503,7 +15592,7 @@ decode-uri-component@^0.2.0, decode-uri-component@^0.2.2: resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== -decompress-response@^3.2.0, decompress-response@^3.3.0: +decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" integrity sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA== @@ -15517,59 +15606,6 @@ decompress-response@^6.0.0: dependencies: mimic-response "^3.1.0" -decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1" - integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ== - dependencies: - file-type "^5.2.0" - is-stream "^1.1.0" - tar-stream "^1.5.2" - -decompress-tarbz2@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b" - integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A== - dependencies: - decompress-tar "^4.1.0" - file-type "^6.1.0" - is-stream "^1.1.0" - seek-bzip "^1.0.5" - unbzip2-stream "^1.0.9" - -decompress-targz@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee" - integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w== - dependencies: - decompress-tar "^4.1.1" - file-type "^5.2.0" - is-stream "^1.1.0" - -decompress-unzip@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69" - integrity sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw== - dependencies: - file-type "^3.8.0" - get-stream "^2.2.0" - pify "^2.3.0" - yauzl "^2.4.2" - -decompress@^4.0.0, decompress@^4.2.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.1.tgz#007f55cc6a62c055afa37c07eb6a4ee1b773f118" - integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ== - dependencies: - decompress-tar "^4.0.0" - decompress-tarbz2 "^4.0.0" - decompress-targz "^4.0.0" - decompress-unzip "^4.0.1" - graceful-fs "^4.1.10" - make-dir "^1.0.0" - pify "^2.3.0" - strip-dirs "^2.0.0" - dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" @@ -16284,41 +16320,6 @@ dotenv@^8.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== -download@^6.2.2: - version "6.2.5" - resolved "https://registry.yarnpkg.com/download/-/download-6.2.5.tgz#acd6a542e4cd0bb42ca70cfc98c9e43b07039714" - integrity sha512-DpO9K1sXAST8Cpzb7kmEhogJxymyVUd5qz/vCOSyvwtp2Klj2XcDt5YUuasgxka44SxF0q5RriKIwJmQHG2AuA== - dependencies: - caw "^2.0.0" - content-disposition "^0.5.2" - decompress "^4.0.0" - ext-name "^5.0.0" - file-type "5.2.0" - filenamify "^2.0.0" - get-stream "^3.0.0" - got "^7.0.0" - make-dir "^1.0.0" - p-event "^1.0.0" - pify "^3.0.0" - -download@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/download/-/download-7.1.0.tgz#9059aa9d70b503ee76a132897be6dec8e5587233" - integrity sha512-xqnBTVd/E+GxJVrX5/eUJiLYjCGPwMpdL+jGhGU57BvtcA7wwhtHVbXBeUk51kOpW3S7Jn3BQbN9Q1R1Km2qDQ== - dependencies: - archive-type "^4.0.0" - caw "^2.0.1" - content-disposition "^0.5.2" - decompress "^4.2.0" - ext-name "^5.0.0" - file-type "^8.1.0" - filenamify "^2.0.0" - get-stream "^3.0.0" - got "^8.3.1" - make-dir "^1.2.0" - p-event "^2.1.0" - pify "^3.0.0" - dset@^3.1.1, dset@^3.1.2: version "3.1.4" resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.4.tgz#f8eaf5f023f068a036d08cd07dc9ffb7d0065248" @@ -16603,7 +16604,7 @@ env-paths@^3.0.0: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-3.0.0.tgz#2f1e89c2f6dbd3408e1b1711dd82d62e317f58da" integrity sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A== -envinfo@^7.7.3, envinfo@^7.7.4: +envinfo@^7.7.4: version "7.14.0" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.14.0.tgz#26dac5db54418f2a4c1159153a0b2ae980838aae" integrity sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg== @@ -17170,7 +17171,7 @@ escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== @@ -18081,17 +18082,6 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -exec-buffer@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/exec-buffer/-/exec-buffer-3.2.0.tgz#b1686dbd904c7cf982e652c1f5a79b1e5573082b" - integrity sha512-wsiD+2Tp6BWHoVv3B+5Dcx6E7u5zky+hUwOHjuH2hKSLR3dvRmX8fk8UD8uqQixHs4Wk6eDmiegVrMPjKj7wpA== - dependencies: - execa "^0.7.0" - p-finally "^1.0.0" - pify "^3.0.0" - rimraf "^2.5.4" - tempfile "^2.0.0" - exec-sh@^0.3.2: version "0.3.6" resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" @@ -18128,19 +18118,6 @@ execa@5.1.1, execa@^5.0.0, execa@^5.1.1: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -execa@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" - integrity sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw== - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" @@ -18200,13 +18177,6 @@ execa@^8.0.1: signal-exit "^4.1.0" strip-final-newline "^3.0.0" -executable@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" - integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== - dependencies: - pify "^2.2.0" - exenv@^1.2.0: version "1.2.2" resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" @@ -18315,21 +18285,6 @@ express@^4.14.0, express@^4.17.1, express@^4.17.3, express@^4.18.2, express@^4.2 utils-merge "1.0.1" vary "~1.1.2" -ext-list@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37" - integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA== - dependencies: - mime-db "^1.28.0" - -ext-name@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ext-name/-/ext-name-5.0.0.tgz#70781981d183ee15d13993c8822045c506c8f0a6" - integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ== - dependencies: - ext-list "^2.0.0" - sort-keys-length "^1.0.0" - ext@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" @@ -18502,18 +18457,6 @@ fast-url-parser@1.1.3, fast-url-parser@^1.1.3: dependencies: punycode "^1.3.2" -fast-xml-parser@^4.1.3: - version "4.5.0" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz#2882b7d01a6825dfdf909638f2de0256351def37" - integrity sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg== - dependencies: - strnum "^1.0.5" - -fastest-levenshtein@^1.0.12: - version "1.0.16" - resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" - integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== - fastq@^1.6.0: version "1.17.1" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" @@ -18574,6 +18517,11 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +fdir@^6.1.1: + version "6.3.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.3.0.tgz#fcca5a23ea20e767b15e081ee13b3e6488ee0bb0" + integrity sha512-QOnuT+BOtivR77wYvCWHfGt9s4Pz1VIMbD463vegT5MLqNXy8rYFT/lPVEqf/bhYeT6qmqrNHhsX+rWwe3rOCQ== + feed@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/feed/-/feed-4.2.2.tgz#865783ef6ed12579e2c44bbef3c9113bc4956a7e" @@ -18591,14 +18539,6 @@ figgy-pudding@^3.5.1: resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== -figures@^1.3.5: - version "1.7.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" - integrity sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ== - dependencies: - escape-string-regexp "^1.0.5" - object-assign "^4.1.0" - figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -18641,40 +18581,11 @@ file-system-cache@^1.0.5: fs-extra "^10.1.0" ramda "^0.28.0" -file-type@3.9.0, file-type@^3.8.0: +file-type@3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" integrity sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA== -file-type@5.2.0, file-type@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6" - integrity sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ== - -file-type@^16.5.3: - version "16.5.4" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.5.4.tgz#474fb4f704bee427681f98dd390058a172a6c2fd" - integrity sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw== - dependencies: - readable-web-to-node-stream "^3.0.0" - strtok3 "^6.2.4" - token-types "^4.1.1" - -file-type@^4.2.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-4.4.0.tgz#1b600e5fca1fbdc6e80c0a70c71c8dba5f7906c5" - integrity sha512-f2UbFQEk7LXgWpi5ntcO86OeA/cC80fuDDDaX/fZ2ZGel+AF7leRQqBBW1eJNiiQkrZlAoM6P+VYP5P6bOlDEQ== - -file-type@^6.1.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919" - integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg== - -file-type@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-8.1.0.tgz#244f3b7ef641bbe0cca196c7276e4b332399f68c" - integrity sha512-qyQ0pzAy78gVoJsmYeNgl8uH8yKhr1lVhW7JbzJmnlRi0I4R2eEDEJZVKG8agpDnLpacwNbDhLNG/LMdxHD2YQ== - file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -18687,20 +18598,6 @@ filelist@^1.0.4: dependencies: minimatch "^5.0.1" -filename-reserved-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229" - integrity sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ== - -filenamify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-2.1.0.tgz#88faf495fb1b47abfd612300002a16228c677ee9" - integrity sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA== - dependencies: - filename-reserved-regex "^2.0.0" - strip-outer "^1.0.0" - trim-repeated "^1.0.0" - filesize@^8.0.6: version "8.0.7" resolved "https://registry.yarnpkg.com/filesize/-/filesize-8.0.7.tgz#695e70d80f4e47012c132d57a059e80c6b580bd8" @@ -18755,7 +18652,7 @@ find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: make-dir "^2.0.0" pkg-dir "^3.0.0" -find-cache-dir@^3.2.0, find-cache-dir@^3.3.1: +find-cache-dir@^3.2.0, find-cache-dir@^3.3.1, find-cache-dir@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== @@ -18830,13 +18727,6 @@ find-up@^6.3.0: locate-path "^7.1.0" path-exists "^5.0.0" -find-versions@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-3.2.0.tgz#10297f98030a786829681690545ef659ed1d254e" - integrity sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww== - dependencies: - semver-regex "^2.0.0" - find-yarn-workspace-root2@1.2.16: version "1.2.16" resolved "https://registry.yarnpkg.com/find-yarn-workspace-root2/-/find-yarn-workspace-root2-1.2.16.tgz#60287009dd2f324f59646bdb4b7610a6b301c2a9" @@ -19066,7 +18956,7 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== -from2@^2.1.0, from2@^2.1.1: +from2@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" integrity sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g== @@ -19118,7 +19008,7 @@ fs-extra@^0.30.0: path-is-absolute "^1.0.0" rimraf "^2.2.8" -fs-extra@^10.1.0: +fs-extra@^10.0.0, fs-extra@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== @@ -19353,13 +19243,6 @@ get-port@^5.1.1: resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== -get-proxy@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/get-proxy/-/get-proxy-2.1.0.tgz#349f2b4d91d44c4d4d4e9cba2ad90143fac5ef93" - integrity sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw== - dependencies: - npm-conf "^1.1.0" - get-source@^2.0.12: version "2.0.12" resolved "https://registry.yarnpkg.com/get-source/-/get-source-2.0.12.tgz#0b47d57ea1e53ce0d3a69f4f3d277eb8047da944" @@ -19378,19 +19261,6 @@ get-stdin@^6.0.0: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== -get-stream@3.0.0, get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - integrity sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ== - -get-stream@^2.2.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de" - integrity sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA== - dependencies: - object-assign "^4.0.1" - pinkie-promise "^2.0.0" - get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -19752,18 +19622,6 @@ globby@^10.0.1: merge2 "^1.2.3" slash "^3.0.0" -globby@^12.0.0: - version "12.2.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-12.2.0.tgz#2ab8046b4fba4ff6eede835b29f678f90e3d3c22" - integrity sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA== - dependencies: - array-union "^3.0.1" - dir-glob "^3.0.1" - fast-glob "^3.2.7" - ignore "^5.1.9" - merge2 "^1.4.1" - slash "^4.0.0" - globby@^13.1.1: version "13.2.2" resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" @@ -19876,55 +19734,12 @@ got@^12.1.0: p-cancelable "^3.0.0" responselike "^3.0.0" -got@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" - integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== - dependencies: - decompress-response "^3.2.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-plain-obj "^1.1.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - p-cancelable "^0.3.0" - p-timeout "^1.1.1" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - url-parse-lax "^1.0.0" - url-to-options "^1.0.1" - -got@^8.3.1: - version "8.3.2" - resolved "https://registry.yarnpkg.com/got/-/got-8.3.2.tgz#1d23f64390e97f776cac52e5b936e5f514d2e937" - integrity sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw== - dependencies: - "@sindresorhus/is" "^0.7.0" - cacheable-request "^2.1.1" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - into-stream "^3.1.0" - is-retry-allowed "^1.1.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - mimic-response "^1.0.0" - p-cancelable "^0.4.0" - p-timeout "^2.0.1" - pify "^3.0.0" - safe-buffer "^5.1.1" - timed-out "^4.0.1" - url-parse-lax "^3.0.0" - url-to-options "^1.0.1" - graceful-fs@4.2.10: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.5, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.8, graceful-fs@^4.2.9: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.5, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -20137,13 +19952,6 @@ hardhat@2.22.2: uuid "^8.3.2" ws "^7.4.6" -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg== - dependencies: - ansi-regex "^2.0.0" - has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" @@ -20183,23 +19991,11 @@ has-proto@^1.0.1, has-proto@^1.0.3: resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== -has-symbol-support-x@^1.4.1: - version "1.4.2" - resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" - integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== - has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== -has-to-string-tag-x@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" - integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== - dependencies: - has-symbol-support-x "^1.4.1" - has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" @@ -20765,11 +20561,6 @@ htmlparser2@^9.1.0: domutils "^3.1.0" entities "^4.5.0" -http-cache-semantics@3.8.1: - version "3.8.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" - integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== - http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" @@ -21064,14 +20855,6 @@ ignore@^5.1.1, ignore@^5.1.9, ignore@^5.2.0, ignore@^5.2.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== -image-minimizer-webpack-plugin@^3.8.3: - version "3.8.3" - resolved "https://registry.yarnpkg.com/image-minimizer-webpack-plugin/-/image-minimizer-webpack-plugin-3.8.3.tgz#41b2379f9d8adabf4e4db63c656543fee26dd4c2" - integrity sha512-Ex0cjNJc2FUSuwN7WHNyxkIZINP0M9lrN+uWJznMcsehiM5Z7ELwk+SEkSGEookK1GUd2wf+09jy1PEH5a5XmQ== - dependencies: - schema-utils "^4.2.0" - serialize-javascript "^6.0.1" - image-size@^1.0.2: version "1.1.1" resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.1.1.tgz#ddd67d4dc340e52ac29ce5f546a09f4e29e840ac" @@ -21079,45 +20862,6 @@ image-size@^1.0.2: dependencies: queue "6.0.2" -imagemin-jpegtran@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/imagemin-jpegtran/-/imagemin-jpegtran-7.0.0.tgz#7728f84876362d489b9a1656e0cc8e2009406e6f" - integrity sha512-MJoyTCW8YjMJf56NorFE41SR/WkaGA3IYk4JgvMlRwguJEEd3PnP9UxA8Y2UWjquz8d+On3Ds/03ZfiiLS8xTQ== - dependencies: - exec-buffer "^3.0.0" - is-jpg "^2.0.0" - jpegtran-bin "^5.0.0" - -imagemin-optipng@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/imagemin-optipng/-/imagemin-optipng-8.0.0.tgz#b88e5cf6da25cc8479e07cdf38c3ae0479df7ef2" - integrity sha512-CUGfhfwqlPjAC0rm8Fy+R2DJDBGjzy2SkfyT09L8rasnF9jSoHFqJ1xxSZWK6HVPZBMhGPMxCTL70OgTHlLF5A== - dependencies: - exec-buffer "^3.0.0" - is-png "^2.0.0" - optipng-bin "^7.0.0" - -imagemin-svgo@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/imagemin-svgo/-/imagemin-svgo-10.0.1.tgz#bc592950831c13998a40cb248f6e82e0b0b5c3dd" - integrity sha512-v27/UTGkb3vrm5jvjsMGQ2oxaDfSOTBfJOgmFO2fYepx05bY1IqWCK13aDytVR+l9w9eOlq0NMCLbxJlghYb2g== - dependencies: - is-svg "^4.3.1" - svgo "^2.5.0" - -imagemin@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/imagemin/-/imagemin-8.0.1.tgz#8b29ecb78197d8f0eac6a782f2e6b38fb3780d9e" - integrity sha512-Q/QaPi+5HuwbZNtQRqUVk6hKacI6z9iWiCSQBisAv7uBynZwO7t1svkryKl7+iSQbkU/6t9DWnHz04cFs2WY7w== - dependencies: - file-type "^16.5.3" - globby "^12.0.0" - graceful-fs "^4.2.8" - junk "^3.1.0" - p-pipe "^4.0.0" - replace-ext "^2.0.0" - slash "^3.0.0" - immer@^10.0.3: version "10.1.1" resolved "https://registry.yarnpkg.com/immer/-/immer-10.1.1.tgz#206f344ea372d8ea176891545ee53ccc062db7bc" @@ -21165,11 +20909,6 @@ import-from@^3.0.0: dependencies: resolve-from "^5.0.0" -import-lazy@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-3.1.0.tgz#891279202c8a2280fdbd6674dbd8da1a1dfc67cc" - integrity sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ== - import-lazy@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" @@ -21366,11 +21105,6 @@ interpret@^2.2.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== -interpret@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" - integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== - intl-messageformat@^10.5.14: version "10.5.14" resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.5.14.tgz#e5bb373f8a37b88fbe647d7b941f3ab2a37ed00a" @@ -21381,14 +21115,6 @@ intl-messageformat@^10.5.14: "@formatjs/icu-messageformat-parser" "2.7.8" tslib "^2.4.0" -into-stream@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6" - integrity sha512-TcdjPibTksa1NQximqep2r17ISRiNE9fwlfbg3F8ANdvP5/yrFTew86VcO//jk4QTaMlbjypPBq76HN2zaKfZQ== - dependencies: - from2 "^2.1.1" - p-is-promise "^1.1.0" - invariant@2.2.4, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -21776,11 +21502,6 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== -is-jpg@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-jpg/-/is-jpg-2.0.0.tgz#2e1997fa6e9166eaac0242daae443403e4ef1d97" - integrity sha512-ODlO0ruzhkzD3sdynIainVP5eoOFNN85rxA1+cwwnPe4dKyX0r5+hxNO5XpCrxlHcmb9vkOit9mhRD2JVuimHg== - is-lambda@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" @@ -21811,11 +21532,6 @@ is-nan@^1.3.2: call-bind "^1.0.0" define-properties "^1.1.3" -is-natural-number@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" - integrity sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ== - is-negative-zero@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" @@ -21902,11 +21618,6 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" -is-png@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-png/-/is-png-2.0.0.tgz#ee8cbc9e9b050425cedeeb4a6fb74a649b0a4a8d" - integrity sha512-4KPGizaVGj2LK7xwJIz8o5B2ubu1D/vcQsgOGFEDlpcvgZHto4gBnyd0ig7Ws+67ixmwKoNmu0hYnpo6AaKb5g== - is-reference@1.2.1, is-reference@^1.1.2: version "1.2.1" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" @@ -21941,11 +21652,6 @@ is-relative@^1.0.0: dependencies: is-unc-path "^1.0.0" -is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" - integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== - is-root@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" @@ -21970,7 +21676,7 @@ is-ssh@^1.3.0: dependencies: protocols "^2.0.1" -is-stream@^1.0.0, is-stream@^1.1.0: +is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== @@ -21999,13 +21705,6 @@ is-subdir@^1.1.1: dependencies: better-path-resolve "1.0.0" -is-svg@^4.3.1: - version "4.4.0" - resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-4.4.0.tgz#34db20a38146be5f2b3060154da33d11e6f74b7c" - integrity sha512-v+AgVwiK5DsGtT9ng+m4mClp6zDAmwrW8nZi6Gg15qzvBnRWWdfWA1TGaXyCDnWq5g5asofIgMVl3PjKxvk1ug== - dependencies: - fast-xml-parser "^4.1.3" - is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" @@ -22274,14 +21973,6 @@ istanbul-reports@^3.0.2, istanbul-reports@^3.1.3, istanbul-reports@^3.1.4: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -isurl@^1.0.0-alpha5: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" - integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== - dependencies: - has-to-string-tag-x "^1.2.0" - is-object "^1.0.1" - iterate-iterator@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/iterate-iterator/-/iterate-iterator-1.0.2.tgz#551b804c9eaa15b847ea6a7cdc2f5bf1ec150f91" @@ -23186,15 +22877,6 @@ jose@^5.0.0: resolved "https://registry.yarnpkg.com/jose/-/jose-5.9.2.tgz#22a22da06edb8fb9e583aa24bafc1e8457b4db92" integrity sha512-ILI2xx/I57b20sd7rHZvgiiQrmp2mcotwsAH+5ajbpFQbrYVQdNHYlQhoA5cFb78CgtBOxtC05TeA+mcgkuCqQ== -jpegtran-bin@^5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/jpegtran-bin/-/jpegtran-bin-5.0.2.tgz#5870fd7e68317bd203a1c94572bd06ae7732cac3" - integrity sha512-4FSmgIcr8d5+V6T1+dHbPZjaFH0ogVyP4UVsE+zri7S9YLO4qAT2our4IN3sW3STVgNTbqPermdIgt2XuAJ4EA== - dependencies: - bin-build "^3.0.0" - bin-wrapper "^4.0.0" - logalot "^2.0.0" - jpjs@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/jpjs/-/jpjs-1.2.1.tgz#f343833de8838a5beba1f42d5a219be0114c44b7" @@ -23505,13 +23187,6 @@ keccak@^3.0.0, keccak@^3.0.2, keccak@^3.0.3: node-gyp-build "^4.2.0" readable-stream "^3.6.0" -keyv@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" - integrity sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA== - dependencies: - json-buffer "3.0.0" - keyv@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" @@ -24068,14 +23743,6 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" -logalot@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/logalot/-/logalot-2.1.0.tgz#5f8e8c90d304edf12530951a5554abb8c5e3f552" - integrity sha512-Ah4CgdSRfeCJagxQhcVNMi9BfGYyEKLa6d7OA6xSbld/Hg3Cf2QiOa1mDpmG7Ve8LOH6DN3mdttzjQAvWTyVkw== - dependencies: - figures "^1.3.5" - squeak "^1.0.0" - logrocket-react@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/logrocket-react/-/logrocket-react-6.0.3.tgz#62c2ce18b9197058f5ba60e54e3f2b084d6588ee" @@ -24103,11 +23770,6 @@ longest-streak@^3.0.0: resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== -longest@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" - integrity sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg== - loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -24144,11 +23806,6 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" -lowercase-keys@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" - integrity sha512-RPlX0+PHuvxVDZ7xX+EBVAp4RsVxP/TdDSN2mJYdiq1Lc4Hz7EUSjUI7RZrKKlmrIzVhf6Jo2stj7++gVarS0A== - lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" @@ -24164,16 +23821,6 @@ lowercase-keys@^3.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== -lpad-align@^1.0.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/lpad-align/-/lpad-align-1.1.2.tgz#21f600ac1c3095c3c6e497ee67271ee08481fe9e" - integrity sha512-MMIcFmmR9zlGZtBcFOows6c2COMekHCIFJz3ew/rRpKZ1wR4mXDPzvcVqLarux8M33X4TPSq2Jdw8WJj0q0KbQ== - dependencies: - get-stdin "^4.0.1" - indent-string "^2.1.0" - longest "^1.0.0" - meow "^3.3.0" - lru-cache@^10.0.0, lru-cache@^10.2.0, lru-cache@^10.4.3: version "10.4.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" @@ -24230,13 +23877,6 @@ magic-string@^0.30.10, magic-string@^0.30.11, magic-string@^0.30.3, magic-string dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" -make-dir@^1.0.0, make-dir@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" - integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== - dependencies: - pify "^3.0.0" - make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -24914,7 +24554,7 @@ memorystream@^0.3.1: resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== -meow@^3.1.0, meow@^3.3.0: +meow@^3.1.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" integrity sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA== @@ -25714,7 +25354,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -"mime-db@>= 1.43.0 < 2", mime-db@^1.28.0: +"mime-db@>= 1.43.0 < 2": version "1.53.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.53.0.tgz#3cb63cd820fc29896d9d4e8c32ab4fcd74ccb447" integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg== @@ -26779,15 +26419,6 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== -normalize-url@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" - integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw== - dependencies: - prepend-http "^2.0.0" - query-string "^5.0.1" - sort-keys "^2.0.0" - normalize-url@^4.1.0: version "4.5.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" @@ -26810,14 +26441,6 @@ npm-bundled@^1.1.1: dependencies: npm-normalize-package-bin "^1.0.1" -npm-conf@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" - integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== - dependencies: - config-chain "^1.1.11" - pify "^3.0.0" - npm-install-checks@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-4.0.0.tgz#a37facc763a2fde0497ef2c6d0ac7c3fbe00d7b4" @@ -27390,14 +27013,6 @@ optionator@^0.9.3: type-check "^0.4.0" word-wrap "^1.2.5" -optipng-bin@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/optipng-bin/-/optipng-bin-7.0.1.tgz#beb8e55a52f8a26f885ee57ab44fcf62397d6972" - integrity sha512-W99mpdW7Nt2PpFiaO+74pkht7KEqkXkeRomdWXfEz3SALZ6hns81y/pm1dsGZ6ItUIfchiNIP6ORDr1zETU1jA== - dependencies: - bin-build "^3.0.0" - bin-wrapper "^4.0.0" - ora@^4.0.3: version "4.1.1" resolved "https://registry.yarnpkg.com/ora/-/ora-4.1.1.tgz#566cc0348a15c36f5f0e979612842e02ba9dddbc" @@ -27432,13 +27047,6 @@ os-browserify@^0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A== -os-filter-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/os-filter-obj/-/os-filter-obj-2.0.0.tgz#1c0b62d5f3a2442749a2d139e6dddee6e81d8d16" - integrity sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg== - dependencies: - arch "^2.1.0" - os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" @@ -27474,16 +27082,6 @@ p-all@^2.1.0: dependencies: p-map "^2.0.0" -p-cancelable@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" - integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== - -p-cancelable@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0" - integrity sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ== - p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -27509,20 +27107,6 @@ p-each-series@^2.1.0: resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== -p-event@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-event/-/p-event-1.3.0.tgz#8e6b4f4f65c72bc5b6fe28b75eda874f96a4a085" - integrity sha512-hV1zbA7gwqPVFcapfeATaNjQ3J0NuzorHPyG8GPL9g/Y/TplWVBVoCKCXL6Ej2zscrCEv195QNWJXuBH6XZuzA== - dependencies: - p-timeout "^1.1.1" - -p-event@^2.1.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/p-event/-/p-event-2.3.1.tgz#596279ef169ab2c3e0cae88c1cfbb08079993ef6" - integrity sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA== - dependencies: - p-timeout "^2.0.1" - p-event@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" @@ -27547,11 +27131,6 @@ p-finally@^2.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw== -p-is-promise@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" - integrity sha512-zL7VE4JVS2IFSkR2GQKDSPEVxkoH43/p7oEnwpdCndKYJO0HVeRB7fA8TJwuLOTBREtK0ea8eHaxdwcpob5dmg== - p-limit@3.1.0, p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" @@ -27615,13 +27194,6 @@ p-locate@^6.0.0: dependencies: p-limit "^4.0.0" -p-map-series@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-1.0.0.tgz#bf98fe575705658a9e1351befb85ae4c1f07bdca" - integrity sha512-4k9LlvY6Bo/1FcIdV33wqZQES0Py+iKISU9Uc8p8AjWoZPnFKMpVIVD3s0EYn4jzLh1I+WeUZkJ0Yoa4Qfw3Kg== - dependencies: - p-reduce "^1.0.0" - p-map-series@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" @@ -27651,11 +27223,6 @@ p-pipe@^3.1.0: resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-3.1.0.tgz#48b57c922aa2e1af6a6404cb7c6bf0eb9cc8e60e" integrity sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw== -p-pipe@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-4.0.0.tgz#7e5424569351b2ab452a47826acb93ce09ad6a2c" - integrity sha512-HkPfFklpZQPUKBFXzKFB6ihLriIHxnmuQdK9WmLDwe4hf2PdhhfWT/FJa+pc3bA1ywvKXtedxIRmd4Y7BTXE4w== - p-queue@^6.6.2: version "6.6.2" resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" @@ -27672,11 +27239,6 @@ p-queue@^7.4.1: eventemitter3 "^5.0.1" p-timeout "^5.0.2" -p-reduce@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" - integrity sha512-3Tx1T3oM1xO/Y8Gj0sWyE78EIJZ+t+aEmXUdvQgvGmSMri7aPTHoovbXEreWKkL5j21Er60XAWLTzKbAKYOujQ== - p-reduce@^2.0.0, p-reduce@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-2.1.0.tgz#09408da49507c6c274faa31f28df334bc712b64a" @@ -27690,20 +27252,6 @@ p-retry@^4.5.0: "@types/retry" "0.12.0" retry "^0.13.1" -p-timeout@^1.1.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" - integrity sha512-gb0ryzr+K2qFqFv6qi3khoeqMZF/+ajxQipEF6NteZVnvz9tzdsfAVj3lYtn1gAXvH5lfLwfxEII799gt/mRIA== - dependencies: - p-finally "^1.0.0" - -p-timeout@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" - integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA== - dependencies: - p-finally "^1.0.0" - p-timeout@^3.0.0, p-timeout@^3.1.0, p-timeout@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" @@ -28186,11 +27734,6 @@ pcre-to-regexp@^1.1.0: resolved "https://registry.yarnpkg.com/pcre-to-regexp/-/pcre-to-regexp-1.1.0.tgz#1c48373d194b982e1416031b41470839fab3ad6c" integrity sha512-KF9XxmUQJ2DIlMj3TqNqY1AWvyvTuIuq11CuuekxyaYMiFuMKGgQrePYMX5bXKLhLG3sDI4CsGAYHPaT7VV7+g== -peek-readable@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-4.1.0.tgz#4ece1111bf5c2ad8867c314c81356847e8a62e72" - integrity sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg== - pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -28304,7 +27847,7 @@ picomatch@^2.0.4, picomatch@^2.0.7, picomatch@^2.2.1, picomatch@^2.2.2, picomatc resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: +pify@^2.0.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== @@ -29261,11 +28804,6 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== -prepend-http@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - integrity sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg== - prepend-http@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" @@ -30367,7 +29905,7 @@ read@1, read@~1.0.1: dependencies: mute-stream "~0.0.4" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@^2.3.8, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@^2.3.8, readable-stream@~2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -30410,13 +29948,6 @@ readable-stream@~1.0.31: isarray "0.0.1" string_decoder "~0.10.x" -readable-web-to-node-stream@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb" - integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw== - dependencies: - readable-stream "^3.6.0" - readdir-scoped-modules@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" @@ -30510,13 +30041,6 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" -rechoir@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" - integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== - dependencies: - resolve "^1.20.0" - recursive-readdir@^2.2.2: version "2.2.3" resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz#e726f328c0d69153bcabd5c322d3195252379372" @@ -30997,11 +30521,6 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -replace-ext@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-2.0.0.tgz#9471c213d22e1bcc26717cd6e50881d88f812b06" - integrity sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug== - request-promise-core@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" @@ -31174,7 +30693,7 @@ response-iterator@^0.2.6: resolved "https://registry.yarnpkg.com/response-iterator/-/response-iterator-0.2.6.tgz#249005fb14d2e4eeb478a3f735a28fd8b4c9f3da" integrity sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw== -responselike@1.0.2, responselike@^1.0.2: +responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" integrity sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ== @@ -31362,6 +30881,17 @@ rollup-plugin-typescript2@^0.27.3: resolve "1.17.0" tslib "2.0.1" +rollup-plugin-typescript2@^0.36.0: + version "0.36.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.36.0.tgz#309564eb70d710412f5901344ca92045e180ed53" + integrity sha512-NB2CSQDxSe9+Oe2ahZbf+B4bh7pHwjV5L+RSYpCu7Q5ROuN94F9b6ioWwKfz3ueL3KTtmX4o2MUH2cgHDIEUsw== + dependencies: + "@rollup/pluginutils" "^4.1.2" + find-cache-dir "^3.3.2" + fs-extra "^10.0.0" + semver "^7.5.4" + tslib "^2.6.2" + rollup-plugin-visualizer@^5.9.2: version "5.12.0" resolved "https://registry.yarnpkg.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.12.0.tgz#661542191ce78ee4f378995297260d0c1efb1302" @@ -31420,6 +30950,31 @@ rollup@^4.20.0: "@rollup/rollup-win32-x64-msvc" "4.21.3" fsevents "~2.3.2" +rollup@^4.22.4: + version "4.22.4" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.22.4.tgz#4135a6446671cd2a2453e1ad42a45d5973ec3a0f" + integrity sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A== + dependencies: + "@types/estree" "1.0.5" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.22.4" + "@rollup/rollup-android-arm64" "4.22.4" + "@rollup/rollup-darwin-arm64" "4.22.4" + "@rollup/rollup-darwin-x64" "4.22.4" + "@rollup/rollup-linux-arm-gnueabihf" "4.22.4" + "@rollup/rollup-linux-arm-musleabihf" "4.22.4" + "@rollup/rollup-linux-arm64-gnu" "4.22.4" + "@rollup/rollup-linux-arm64-musl" "4.22.4" + "@rollup/rollup-linux-powerpc64le-gnu" "4.22.4" + "@rollup/rollup-linux-riscv64-gnu" "4.22.4" + "@rollup/rollup-linux-s390x-gnu" "4.22.4" + "@rollup/rollup-linux-x64-gnu" "4.22.4" + "@rollup/rollup-linux-x64-musl" "4.22.4" + "@rollup/rollup-win32-arm64-msvc" "4.22.4" + "@rollup/rollup-win32-ia32-msvc" "4.22.4" + "@rollup/rollup-win32-x64-msvc" "4.22.4" + fsevents "~2.3.2" + rsvp@^4.8.4: version "4.8.5" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" @@ -31711,13 +31266,6 @@ section-matter@^1.0.0: extend-shallow "^2.0.1" kind-of "^6.0.0" -seek-bzip@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4" - integrity sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ== - dependencies: - commander "^2.8.1" - select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -31743,19 +31291,7 @@ semver-diff@^4.0.0: dependencies: semver "^7.3.5" -semver-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-2.0.0.tgz#a93c2c5844539a770233379107b38c7b4ac9d338" - integrity sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw== - -semver-truncate@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/semver-truncate/-/semver-truncate-1.1.2.tgz#57f41de69707a62709a7e0104ba2117109ea47e8" - integrity sha512-V1fGg9i4CL3qesB6U0L6XAm4xOJiHmt4QAacazumuasc03BvtFGIMCduv01JWQ69Nv+JST9TqhSCiJoxoY031w== - dependencies: - semver "^5.3.0" - -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== @@ -32481,20 +32017,6 @@ sort-css-media-queries@2.2.0: resolved "https://registry.yarnpkg.com/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz#aa33cf4a08e0225059448b6c40eddbf9f1c8334c" integrity sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA== -sort-keys-length@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188" - integrity sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw== - dependencies: - sort-keys "^1.0.0" - -sort-keys@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" - integrity sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg== - dependencies: - is-plain-obj "^1.0.0" - sort-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" @@ -32725,15 +32247,6 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== -squeak@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/squeak/-/squeak-1.3.0.tgz#33045037b64388b567674b84322a6521073916c3" - integrity sha512-YQL1ulInM+ev8nXX7vfXsCsDh6IqXlrremc1hzi77776BtpWgYJUMto3UM05GSAaGzJgWekszjoKDrVNB5XG+A== - dependencies: - chalk "^1.0.0" - console-stream "^0.1.1" - lpad-align "^1.0.1" - srcset@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/srcset/-/srcset-4.0.0.tgz#336816b665b14cd013ba545b6fe62357f86e65f4" @@ -33233,13 +32746,6 @@ strip-bom@^4.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== -strip-dirs@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5" - integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g== - dependencies: - is-natural-number "^4.0.1" - strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" @@ -33291,18 +32797,6 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== -strip-outer@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" - integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg== - dependencies: - escape-string-regexp "^1.0.2" - -strnum@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" - integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== - strong-log-transformer@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" @@ -33312,14 +32806,6 @@ strong-log-transformer@^2.1.0: minimist "^1.2.0" through "^2.3.4" -strtok3@^6.2.4: - version "6.3.0" - resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-6.3.0.tgz#358b80ffe6d5d5620e19a073aa78ce947a90f9a0" - integrity sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw== - dependencies: - "@tokenizer/token" "^0.3.0" - peek-readable "^4.1.0" - stubborn-fs@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/stubborn-fs/-/stubborn-fs-1.2.5.tgz#e5e244223166921ddf66ed5e062b6b3bf285bfd2" @@ -33472,11 +32958,6 @@ supports-color@8.1.1, supports-color@^8.0.0, supports-color@^8.1.1: dependencies: has-flag "^4.0.0" -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== - supports-color@^3.1.0: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" @@ -33537,7 +33018,7 @@ svg-tags@1: resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA== -svgo@^2.5.0, svgo@^2.7.0, svgo@^2.8.0: +svgo@^2.7.0, svgo@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== @@ -33768,19 +33249,6 @@ tar-fs@^2.0.0, tar-fs@^2.1.1: pump "^3.0.0" tar-stream "^2.1.4" -tar-stream@^1.5.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" - integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== - dependencies: - bl "^1.0.0" - buffer-alloc "^1.2.0" - end-of-stream "^1.0.0" - fs-constants "^1.0.0" - readable-stream "^2.3.0" - to-buffer "^1.1.1" - xtend "^4.0.0" - tar-stream@^2.1.4: version "2.2.0" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" @@ -33878,14 +33346,6 @@ temp-write@^4.0.0: temp-dir "^1.0.0" uuid "^3.3.2" -tempfile@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/tempfile/-/tempfile-2.0.0.tgz#6b0446856a9b1114d1856ffcbe509cccb0977265" - integrity sha512-ZOn6nJUgvgC09+doCEF3oB+r3ag7kUvlsXEGX069QRD60p+P3uP7XG9N2/at+EyIRGSN//ZY3LyEotA1YpmjuA== - dependencies: - temp-dir "^1.0.0" - uuid "^3.0.1" - term-size@^2.1.0: version "2.2.1" resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" @@ -34043,7 +33503,7 @@ time-span@4.0.0: dependencies: convert-hrtime "^3.0.0" -timed-out@^4.0.0, timed-out@^4.0.1: +timed-out@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA== @@ -34097,11 +33557,6 @@ to-arraybuffer@^1.0.0: resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" integrity sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA== -to-buffer@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" - integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== - to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -34159,14 +33614,6 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== -token-types@^4.1.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/token-types/-/token-types-4.2.1.tgz#0f897f03665846982806e138977dbe72d44df753" - integrity sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ== - dependencies: - "@tokenizer/token" "^0.3.0" - ieee754 "^1.2.1" - totalist@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" @@ -34242,13 +33689,6 @@ trim-newlines@^3.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -trim-repeated@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" - integrity sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg== - dependencies: - escape-string-regexp "^1.0.2" - trim-trailing-lines@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0" @@ -34619,6 +34059,11 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tunnel@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" + integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== + tweetnacl-util@^0.15.1: version "0.15.1" resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" @@ -34951,14 +34396,6 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -unbzip2-stream@^1.0.9: - version "1.4.3" - resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" - integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== - dependencies: - buffer "^5.2.1" - through "^2.3.8" - unc-path-regex@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" @@ -34991,7 +34428,7 @@ undici@5.26.5: dependencies: "@fastify/busboy" "^2.0.0" -undici@^5.14.0, undici@^5.28.4: +undici@^5.14.0, undici@^5.25.4, undici@^5.28.4: version "5.28.4" resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== @@ -35455,13 +34892,6 @@ url-loader@^4.1.1: mime-types "^2.1.27" schema-utils "^3.0.0" -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" - integrity sha512-BVA4lR5PIviy2PMseNd2jbFQ+jwSwQGdJejf5ctd1rEXt0Ypd7yanUK9+lYechVlN5VaTJGsu2U/3MDDu6KgBA== - dependencies: - prepend-http "^1.0.1" - url-parse-lax@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" @@ -35474,11 +34904,6 @@ url-set-query@^1.0.0: resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" integrity sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg== -url-to-options@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" - integrity sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A== - url@^0.11.0: version "0.11.4" resolved "https://registry.yarnpkg.com/url/-/url-0.11.4.tgz#adca77b3562d56b72746e76b330b7f27b6721f3c" @@ -35646,7 +35071,7 @@ uuid@8.3.2, uuid@^8.0.0, uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -uuid@^3.0.1, uuid@^3.3.2: +uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== @@ -36855,25 +36280,6 @@ webpack-bundle-analyzer@^4.9.0: sirv "^2.0.3" ws "^7.3.1" -webpack-cli@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" - integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== - dependencies: - "@discoveryjs/json-ext" "^0.5.0" - "@webpack-cli/configtest" "^2.1.1" - "@webpack-cli/info" "^2.0.2" - "@webpack-cli/serve" "^2.0.5" - colorette "^2.0.14" - commander "^10.0.1" - cross-spawn "^7.0.3" - envinfo "^7.7.3" - fastest-levenshtein "^1.0.12" - import-local "^3.0.2" - interpret "^3.1.1" - rechoir "^0.8.0" - webpack-merge "^5.7.3" - webpack-dev-middleware@^3.7.3: version "3.7.3" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5" @@ -36966,7 +36372,7 @@ webpack-log@^2.0.0: ansi-colors "^3.0.0" uuid "^3.3.2" -webpack-merge@^5.7.3, webpack-merge@^5.9.0: +webpack-merge@^5.9.0: version "5.10.0" resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== @@ -37034,7 +36440,7 @@ webpack@4: watchpack "^1.7.4" webpack-sources "^1.4.1" -"webpack@>=4.43.0 <6.0.0", webpack@^5.61.0, webpack@^5.88.1, webpack@^5.89.0, webpack@^5.9.0: +"webpack@>=4.43.0 <6.0.0", webpack@^5.61.0, webpack@^5.88.1, webpack@^5.9.0: version "5.94.0" resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.94.0.tgz#77a6089c716e7ab90c1c67574a28da518a20970f" integrity sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg== @@ -37711,7 +37117,7 @@ yauzl-promise@2.1.3: yauzl "^2.9.1" yauzl-clone "^1.0.4" -yauzl@^2.4.2, yauzl@^2.9.1: +yauzl@^2.9.1: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== From 9d1083c1b5c542af3bcf5b1ee0c9f2029266f32d Mon Sep 17 00:00:00 2001 From: abtestingalpha <104046418+abtestingalpha@users.noreply.github.com> Date: Tue, 1 Oct 2024 13:00:38 -0400 Subject: [PATCH 70/90] Increments version (#3209) --- packages/synapse-constants/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/synapse-constants/package.json b/packages/synapse-constants/package.json index 151e198c36..b99b946ed9 100644 --- a/packages/synapse-constants/package.json +++ b/packages/synapse-constants/package.json @@ -1,6 +1,6 @@ { "name": "synapse-constants", - "version": "1.3.24", + "version": "1.4.1", "description": "This is an npm package that maintains all synapse constants", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", From 2ecb954c8bc5123f04c96548f90640a47416feb5 Mon Sep 17 00:00:00 2001 From: abtestingalpha Date: Tue, 1 Oct 2024 17:04:59 +0000 Subject: [PATCH 71/90] Publish - @synapsecns/explorer-ui@0.3.5 - synapse-constants@1.5.0 --- packages/explorer-ui/CHANGELOG.md | 8 ++++++++ packages/explorer-ui/package.json | 4 ++-- packages/synapse-constants/CHANGELOG.md | 11 +++++++++++ packages/synapse-constants/package.json | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/explorer-ui/CHANGELOG.md b/packages/explorer-ui/CHANGELOG.md index 10e44ec05a..eb1a764185 100644 --- a/packages/explorer-ui/CHANGELOG.md +++ b/packages/explorer-ui/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.5](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.4...@synapsecns/explorer-ui@0.3.5) (2024-10-01) + +**Note:** Version bump only for package @synapsecns/explorer-ui + + + + + ## [0.3.4](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.3...@synapsecns/explorer-ui@0.3.4) (2024-09-30) **Note:** Version bump only for package @synapsecns/explorer-ui diff --git a/packages/explorer-ui/package.json b/packages/explorer-ui/package.json index ba8c2ff199..4c8f04df55 100644 --- a/packages/explorer-ui/package.json +++ b/packages/explorer-ui/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/explorer-ui", - "version": "0.3.4", + "version": "0.3.5", "private": true, "engines": { "node": ">=18.17.0" @@ -38,7 +38,7 @@ "recharts": "^2.3.2", "sharp": "^0.31.3", "swr": "^1.3.0", - "synapse-constants": "^1.3.24", + "synapse-constants": "^1.5.0", "tailwind-merge": "^1.3.0", "tiny-warning": "^1.0.3", "web-vitals": "^2.1.4" diff --git a/packages/synapse-constants/CHANGELOG.md b/packages/synapse-constants/CHANGELOG.md index a229762afc..3142f9c808 100644 --- a/packages/synapse-constants/CHANGELOG.md +++ b/packages/synapse-constants/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.5.0](https://github.com/synapsecns/sanguine/compare/synapse-constants@1.3.24...synapse-constants@1.5.0) (2024-10-01) + + +### Features + +* **synapse-constants:** Refactor with rollup for package build and export [SLT-160] ([#3175](https://github.com/synapsecns/sanguine/issues/3175)) ([32cee8e](https://github.com/synapsecns/sanguine/commit/32cee8e3bb88222e9876b0963effb51d72be31a6)) + + + + + ## [1.3.24](https://github.com/synapsecns/sanguine/compare/synapse-constants@1.3.23...synapse-constants@1.3.24) (2024-09-10) **Note:** Version bump only for package synapse-constants diff --git a/packages/synapse-constants/package.json b/packages/synapse-constants/package.json index b99b946ed9..ba657f1a60 100644 --- a/packages/synapse-constants/package.json +++ b/packages/synapse-constants/package.json @@ -1,6 +1,6 @@ { "name": "synapse-constants", - "version": "1.4.1", + "version": "1.5.0", "description": "This is an npm package that maintains all synapse constants", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", From a014f6f4ea053babba5dcb9556d534dd8dadc2fe Mon Sep 17 00:00:00 2001 From: abtestingalpha <104046418+abtestingalpha@users.noreply.github.com> Date: Tue, 1 Oct 2024 13:39:23 -0400 Subject: [PATCH 72/90] Increments version (#3210) --- packages/synapse-constants/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/synapse-constants/package.json b/packages/synapse-constants/package.json index ba657f1a60..58b3e7b913 100644 --- a/packages/synapse-constants/package.json +++ b/packages/synapse-constants/package.json @@ -1,6 +1,6 @@ { "name": "synapse-constants", - "version": "1.5.0", + "version": "1.5.1", "description": "This is an npm package that maintains all synapse constants", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", From 42ef47392bdff7c93d04162f9648c1b5f63aaea9 Mon Sep 17 00:00:00 2001 From: abtestingalpha Date: Tue, 1 Oct 2024 17:43:31 +0000 Subject: [PATCH 73/90] Publish - @synapsecns/explorer-ui@0.3.6 - synapse-constants@1.5.2 --- packages/explorer-ui/CHANGELOG.md | 8 ++++++++ packages/explorer-ui/package.json | 4 ++-- packages/synapse-constants/CHANGELOG.md | 8 ++++++++ packages/synapse-constants/package.json | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/explorer-ui/CHANGELOG.md b/packages/explorer-ui/CHANGELOG.md index eb1a764185..f911de69e8 100644 --- a/packages/explorer-ui/CHANGELOG.md +++ b/packages/explorer-ui/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.6](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.5...@synapsecns/explorer-ui@0.3.6) (2024-10-01) + +**Note:** Version bump only for package @synapsecns/explorer-ui + + + + + ## [0.3.5](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.4...@synapsecns/explorer-ui@0.3.5) (2024-10-01) **Note:** Version bump only for package @synapsecns/explorer-ui diff --git a/packages/explorer-ui/package.json b/packages/explorer-ui/package.json index 4c8f04df55..c16077eccc 100644 --- a/packages/explorer-ui/package.json +++ b/packages/explorer-ui/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/explorer-ui", - "version": "0.3.5", + "version": "0.3.6", "private": true, "engines": { "node": ">=18.17.0" @@ -38,7 +38,7 @@ "recharts": "^2.3.2", "sharp": "^0.31.3", "swr": "^1.3.0", - "synapse-constants": "^1.5.0", + "synapse-constants": "^1.5.2", "tailwind-merge": "^1.3.0", "tiny-warning": "^1.0.3", "web-vitals": "^2.1.4" diff --git a/packages/synapse-constants/CHANGELOG.md b/packages/synapse-constants/CHANGELOG.md index 3142f9c808..8f2bfab5a3 100644 --- a/packages/synapse-constants/CHANGELOG.md +++ b/packages/synapse-constants/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.5.2](https://github.com/synapsecns/sanguine/compare/synapse-constants@1.5.0...synapse-constants@1.5.2) (2024-10-01) + +**Note:** Version bump only for package synapse-constants + + + + + # [1.5.0](https://github.com/synapsecns/sanguine/compare/synapse-constants@1.3.24...synapse-constants@1.5.0) (2024-10-01) diff --git a/packages/synapse-constants/package.json b/packages/synapse-constants/package.json index 58b3e7b913..bda90b84cb 100644 --- a/packages/synapse-constants/package.json +++ b/packages/synapse-constants/package.json @@ -1,6 +1,6 @@ { "name": "synapse-constants", - "version": "1.5.1", + "version": "1.5.2", "description": "This is an npm package that maintains all synapse constants", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", From aca00efeebe23f2b9a1c89917502bf506e806fb8 Mon Sep 17 00:00:00 2001 From: abtestingalpha <104046418+abtestingalpha@users.noreply.github.com> Date: Tue, 1 Oct 2024 14:10:26 -0400 Subject: [PATCH 74/90] Increments (#3211) --- packages/synapse-constants/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/synapse-constants/package.json b/packages/synapse-constants/package.json index bda90b84cb..fe1a3ceba7 100644 --- a/packages/synapse-constants/package.json +++ b/packages/synapse-constants/package.json @@ -1,6 +1,6 @@ { "name": "synapse-constants", - "version": "1.5.2", + "version": "1.5.3", "description": "This is an npm package that maintains all synapse constants", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", From a9956dada4a1f00c7bf34c9d56f8e2aaa19105a4 Mon Sep 17 00:00:00 2001 From: abtestingalpha Date: Tue, 1 Oct 2024 18:14:52 +0000 Subject: [PATCH 75/90] Publish - @synapsecns/explorer-ui@0.3.7 - synapse-constants@1.5.4 --- packages/explorer-ui/CHANGELOG.md | 8 ++++++++ packages/explorer-ui/package.json | 4 ++-- packages/synapse-constants/CHANGELOG.md | 8 ++++++++ packages/synapse-constants/package.json | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/explorer-ui/CHANGELOG.md b/packages/explorer-ui/CHANGELOG.md index f911de69e8..7fe0e34b16 100644 --- a/packages/explorer-ui/CHANGELOG.md +++ b/packages/explorer-ui/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.7](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.6...@synapsecns/explorer-ui@0.3.7) (2024-10-01) + +**Note:** Version bump only for package @synapsecns/explorer-ui + + + + + ## [0.3.6](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.5...@synapsecns/explorer-ui@0.3.6) (2024-10-01) **Note:** Version bump only for package @synapsecns/explorer-ui diff --git a/packages/explorer-ui/package.json b/packages/explorer-ui/package.json index c16077eccc..5255ba13b6 100644 --- a/packages/explorer-ui/package.json +++ b/packages/explorer-ui/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/explorer-ui", - "version": "0.3.6", + "version": "0.3.7", "private": true, "engines": { "node": ">=18.17.0" @@ -38,7 +38,7 @@ "recharts": "^2.3.2", "sharp": "^0.31.3", "swr": "^1.3.0", - "synapse-constants": "^1.5.2", + "synapse-constants": "^1.5.4", "tailwind-merge": "^1.3.0", "tiny-warning": "^1.0.3", "web-vitals": "^2.1.4" diff --git a/packages/synapse-constants/CHANGELOG.md b/packages/synapse-constants/CHANGELOG.md index 8f2bfab5a3..7db6455005 100644 --- a/packages/synapse-constants/CHANGELOG.md +++ b/packages/synapse-constants/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.5.4](https://github.com/synapsecns/sanguine/compare/synapse-constants@1.5.2...synapse-constants@1.5.4) (2024-10-01) + +**Note:** Version bump only for package synapse-constants + + + + + ## [1.5.2](https://github.com/synapsecns/sanguine/compare/synapse-constants@1.5.0...synapse-constants@1.5.2) (2024-10-01) **Note:** Version bump only for package synapse-constants diff --git a/packages/synapse-constants/package.json b/packages/synapse-constants/package.json index fe1a3ceba7..f4db141fe6 100644 --- a/packages/synapse-constants/package.json +++ b/packages/synapse-constants/package.json @@ -1,6 +1,6 @@ { "name": "synapse-constants", - "version": "1.5.3", + "version": "1.5.4", "description": "This is an npm package that maintains all synapse constants", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", From 5acbc3972904ce5e99b46c5c4a44bdcb03b9e31b Mon Sep 17 00:00:00 2001 From: Moses <103143573+Defi-Moses@users.noreply.github.com> Date: Tue, 1 Oct 2024 22:59:39 +0100 Subject: [PATCH 76/90] bump (#3213) --- packages/synapse-constants/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/synapse-constants/package.json b/packages/synapse-constants/package.json index f4db141fe6..5593401101 100644 --- a/packages/synapse-constants/package.json +++ b/packages/synapse-constants/package.json @@ -1,6 +1,6 @@ { "name": "synapse-constants", - "version": "1.5.4", + "version": "1.5.5", "description": "This is an npm package that maintains all synapse constants", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", From c23856b6c764907c478ef214b4e6207d28c83310 Mon Sep 17 00:00:00 2001 From: Defi-Moses Date: Tue, 1 Oct 2024 22:03:44 +0000 Subject: [PATCH 77/90] Publish - @synapsecns/explorer-ui@0.3.8 - synapse-constants@1.5.6 --- packages/explorer-ui/CHANGELOG.md | 8 ++++++++ packages/explorer-ui/package.json | 4 ++-- packages/synapse-constants/CHANGELOG.md | 8 ++++++++ packages/synapse-constants/package.json | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/explorer-ui/CHANGELOG.md b/packages/explorer-ui/CHANGELOG.md index 7fe0e34b16..87d7ffe6be 100644 --- a/packages/explorer-ui/CHANGELOG.md +++ b/packages/explorer-ui/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.8](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.7...@synapsecns/explorer-ui@0.3.8) (2024-10-01) + +**Note:** Version bump only for package @synapsecns/explorer-ui + + + + + ## [0.3.7](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.6...@synapsecns/explorer-ui@0.3.7) (2024-10-01) **Note:** Version bump only for package @synapsecns/explorer-ui diff --git a/packages/explorer-ui/package.json b/packages/explorer-ui/package.json index 5255ba13b6..2d85249360 100644 --- a/packages/explorer-ui/package.json +++ b/packages/explorer-ui/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/explorer-ui", - "version": "0.3.7", + "version": "0.3.8", "private": true, "engines": { "node": ">=18.17.0" @@ -38,7 +38,7 @@ "recharts": "^2.3.2", "sharp": "^0.31.3", "swr": "^1.3.0", - "synapse-constants": "^1.5.4", + "synapse-constants": "^1.5.6", "tailwind-merge": "^1.3.0", "tiny-warning": "^1.0.3", "web-vitals": "^2.1.4" diff --git a/packages/synapse-constants/CHANGELOG.md b/packages/synapse-constants/CHANGELOG.md index 7db6455005..ea13cbea97 100644 --- a/packages/synapse-constants/CHANGELOG.md +++ b/packages/synapse-constants/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.5.6](https://github.com/synapsecns/sanguine/compare/synapse-constants@1.5.4...synapse-constants@1.5.6) (2024-10-01) + +**Note:** Version bump only for package synapse-constants + + + + + ## [1.5.4](https://github.com/synapsecns/sanguine/compare/synapse-constants@1.5.2...synapse-constants@1.5.4) (2024-10-01) **Note:** Version bump only for package synapse-constants diff --git a/packages/synapse-constants/package.json b/packages/synapse-constants/package.json index 5593401101..ee4e46c105 100644 --- a/packages/synapse-constants/package.json +++ b/packages/synapse-constants/package.json @@ -1,6 +1,6 @@ { "name": "synapse-constants", - "version": "1.5.5", + "version": "1.5.6", "description": "This is an npm package that maintains all synapse constants", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", From 0b16d1cff2591e93400e66000e01c1b009d59282 Mon Sep 17 00:00:00 2001 From: aureliusbtc <82057759+aureliusbtc@users.noreply.github.com> Date: Wed, 2 Oct 2024 11:29:42 -0400 Subject: [PATCH 78/90] update bl --- packages/synapse-interface/public/blacklist.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/synapse-interface/public/blacklist.json b/packages/synapse-interface/public/blacklist.json index 787455be23..a599ade461 100644 --- a/packages/synapse-interface/public/blacklist.json +++ b/packages/synapse-interface/public/blacklist.json @@ -541,5 +541,7 @@ "0x800f06b394da4c98e88a0da09f0fb1eb280f9636", "0xf7e8033366166f92eb477b7b38e0d47d47b43326", "0xc01f4307DAa6A2d9e01303B194E77fBEF29Fe904", - "0xFAF26EdB4E4b43B7A513808d59913FC43A705D74" + "0xFAF26EdB4E4b43B7A513808d59913FC43A705D74", + "0x278dF4492d16321b247660799FAD1A12dE152Dd1", + "0x79c7c3D7CBe8A8354CD1401A8718a7dBF65301d2" ] From e25c656cc502bdee91edbe466831e21eb7f8858c Mon Sep 17 00:00:00 2001 From: aureliusbtc <82057759+aureliusbtc@users.noreply.github.com> Date: Wed, 2 Oct 2024 11:30:29 -0400 Subject: [PATCH 79/90] update bl --- packages/synapse-interface/public/blacklist.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/synapse-interface/public/blacklist.json b/packages/synapse-interface/public/blacklist.json index a599ade461..d7bbe5efe9 100644 --- a/packages/synapse-interface/public/blacklist.json +++ b/packages/synapse-interface/public/blacklist.json @@ -543,5 +543,6 @@ "0xc01f4307DAa6A2d9e01303B194E77fBEF29Fe904", "0xFAF26EdB4E4b43B7A513808d59913FC43A705D74", "0x278dF4492d16321b247660799FAD1A12dE152Dd1", - "0x79c7c3D7CBe8A8354CD1401A8718a7dBF65301d2" + "0x79c7c3D7CBe8A8354CD1401A8718a7dBF65301d2", + "0x3b00Daad3DE070f3060812e0F8D6b470B9F64Fa2" ] From 8f1899d3fed7ef455fc3a6a0f816e2e15eab2714 Mon Sep 17 00:00:00 2001 From: aureliusbtc <82057759+aureliusbtc@users.noreply.github.com> Date: Wed, 2 Oct 2024 11:48:24 -0400 Subject: [PATCH 80/90] update bl --- packages/synapse-interface/public/blacklist.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/synapse-interface/public/blacklist.json b/packages/synapse-interface/public/blacklist.json index d7bbe5efe9..333e118a81 100644 --- a/packages/synapse-interface/public/blacklist.json +++ b/packages/synapse-interface/public/blacklist.json @@ -544,5 +544,8 @@ "0xFAF26EdB4E4b43B7A513808d59913FC43A705D74", "0x278dF4492d16321b247660799FAD1A12dE152Dd1", "0x79c7c3D7CBe8A8354CD1401A8718a7dBF65301d2", - "0x3b00Daad3DE070f3060812e0F8D6b470B9F64Fa2" + "0x3b00Daad3DE070f3060812e0F8D6b470B9F64Fa2", + "0xeB428AdEF88EcFFbd819bD7Afa85d41b7fAa0510", + "0xf97823da25d1e4051ca2bf4ffbe6b788df0628cb", + "0x105c3fB416146c18AD96a470140BEAE8fb9cf950" ] From de34cf0f91333b8dcd3ec41f58d94bd4244f978e Mon Sep 17 00:00:00 2001 From: lawsonkight Date: Wed, 2 Oct 2024 09:22:57 -0700 Subject: [PATCH 81/90] Docs/lk exploration [SLT-170] (#3141) * Refactor of docs * folder organization * sidebar organization * refactor of CCTP and RFQ docs pages * Bridge SDK and Synapse Router * Brand assets page * animated logo v1 * interactive animated logo --------- Co-authored-by: abtestingalpha Co-authored-by: defi-moses Co-authored-by: Moses <103143573+Defi-Moses@users.noreply.github.com> --- docs/bridge/docs/01-About/01-DAO.md | 19 + docs/bridge/docs/01-About/02-Brand-Assets.md | 21 + docs/bridge/docs/01-About/03-Routes.md | 7 + docs/bridge/docs/01-About/index.md | 75 ++ docs/bridge/docs/02-Bridge/01-SDK.md | 222 ++++ docs/bridge/docs/02-Bridge/02-REST-API.md | 205 +++ docs/bridge/docs/02-Bridge/03-Widget.md | 196 +++ .../bridge/docs/02-Bridge/04-Code-Examples.md | 350 ++++++ docs/bridge/docs/02-Bridge/index.md | 58 + docs/bridge/docs/02-Bridge/lp-tokens.svg | 61 + .../04-Routers/01-Synapse-Router/index.md | 299 +++++ docs/bridge/docs/04-Routers/CCTP/index.md | 132 ++ docs/bridge/docs/04-Routers/RFQ/01-Relayer.md | 311 +++++ .../RFQ}/API/get-contract-addresses.api.mdx | 0 .../RFQ}/API/get-quotes.api.mdx | 0 .../API.md => 04-Routers/RFQ/API/index.md} | 8 +- .../RFQ}/API/relay-ack.api.mdx | 0 .../RFQ}/API/upsert-quote.api.mdx | 0 .../RFQ}/API/upsert-quotes.api.mdx | 0 .../Relayer => 04-Routers/RFQ}/dashboard.png | Bin docs/bridge/docs/04-Routers/RFQ/index.md | 77 ++ .../docs/05-Contracts/01-Synapse-Token.md | 33 + .../docs/05-Contracts/02-Synapse-Router.md | 44 + .../05-Contracts/03-Intermediate-Tokens.md | 41 + .../docs/05-Contracts/04-Liquidity-Pools.md | 41 + docs/bridge/docs/05-Contracts/05-CCTP.md | 21 + docs/bridge/docs/05-Contracts/06-RFQ.md | 28 + .../docs/05-Contracts/07-Bridge-Zaps.md | 30 + docs/bridge/docs/05-Contracts/08-MiniChef.md | 29 + .../Scribe.md => 06-Services/01-Scribe.md} | 9 +- .../Omnirpc.md => 06-Services/02-Omnirpc.md} | 12 +- .../Signer.md => 06-Services/03-Signer.md} | 0 .../04-Submitter.md} | 6 +- .../05-Observability.md} | 2 + .../img/signer/aws/create-acces-key.png | Bin .../img/signer/aws/create-access-key.png | Bin .../img/signer/aws/iam-dash.png | Bin .../img/signer/aws/iam-preview-user.png | Bin .../img/signer/aws/key-details.png | Bin .../img/signer/aws/kms-1.png | Bin .../img/signer/aws/kms-2.png | Bin .../img/signer/aws/kms-advanced.png | Bin .../img/signer/aws/kms-labels.png | Bin .../img/signer/aws/kms-permissons.png | Bin .../img/signer/aws/kms-user-permissions.png | Bin .../img/signer/aws/kms-user.png | Bin .../img/signer/aws/perms.png | Bin .../img/signer/aws/review.png | Bin .../img/signer/aws/user-list.png | Bin .../img/signer/aws/user-perms.png | Bin .../img/submitter/metrics.png | Bin .../docs/07-Support/Transaction-Support.md | 51 + docs/bridge/docs/07-Support/index.md | 12 + docs/bridge/docs/CCTP/Contracts.md | 18 - docs/bridge/docs/CCTP/Overview.md | 8 - docs/bridge/docs/CCTP/Relayer.md | 107 -- docs/bridge/docs/CCTP/_category_.json | 8 - docs/bridge/docs/rfq/API/sidebar.ts | 26 - docs/bridge/docs/rfq/Contracts.md | 51 - docs/bridge/docs/rfq/RFQ.md | 24 - docs/bridge/docs/rfq/Relayer/Relayer.md | 282 ----- docs/bridge/docs/rfq/_category_.json | 8 - docs/bridge/docusaurus.config.ts | 122 +- docs/bridge/package.json | 31 +- docs/bridge/sidebars.ts | 12 +- docs/bridge/src/components/AnimatedLogo.tsx | 143 +++ docs/bridge/src/components/BridgeFlow.tsx | 228 ++++ docs/bridge/src/components/CCTPFlow.tsx | 191 +++ .../src/components/HomepageFeatures/index.tsx | 26 +- docs/bridge/src/components/RFQFlow.tsx | 185 +++ docs/bridge/src/components/Routes.tsx | 51 + docs/bridge/src/components/SVGBridge.tsx | 182 +++ docs/bridge/src/components/SVGWidget.tsx | 160 +++ docs/bridge/src/components/USDC.tsx | 7 + docs/bridge/src/css/custom.css | 114 +- docs/bridge/src/pages/index.tsx | 89 +- .../brand-assets/synapse-border-mark.svg | 22 + .../brand-assets/synapse-brand-assets.zip | Bin 0 -> 8516 bytes .../brand-assets/synapse-logo-black.svg | 14 + .../brand-assets/synapse-logo-onDark.svg | 24 + .../brand-assets/synapse-logo-onLight.svg | 24 + .../brand-assets/synapse-logo-white.svg | 14 + .../brand-assets/synapse-mark-black.svg | 7 + .../brand-assets/synapse-mark-white.svg | 7 + .../static/brand-assets/synapse-mark.svg | 13 + .../static/img/docusaurus-social-card.jpg | Bin 55746 -> 0 bytes docs/bridge/static/img/docusaurus.png | Bin 5142 -> 0 bytes docs/bridge/static/img/favicon.ico | Bin 15086 -> 0 bytes docs/bridge/static/img/logo.svg | 9 - .../static/img/undraw_docusaurus_mountain.svg | 171 --- .../static/img/undraw_docusaurus_react.svg | 170 --- .../static/img/undraw_docusaurus_tree.svg | 40 - yarn.lock | 1103 +++++------------ 93 files changed, 4191 insertions(+), 1900 deletions(-) create mode 100644 docs/bridge/docs/01-About/01-DAO.md create mode 100644 docs/bridge/docs/01-About/02-Brand-Assets.md create mode 100644 docs/bridge/docs/01-About/03-Routes.md create mode 100644 docs/bridge/docs/01-About/index.md create mode 100644 docs/bridge/docs/02-Bridge/01-SDK.md create mode 100644 docs/bridge/docs/02-Bridge/02-REST-API.md create mode 100644 docs/bridge/docs/02-Bridge/03-Widget.md create mode 100644 docs/bridge/docs/02-Bridge/04-Code-Examples.md create mode 100644 docs/bridge/docs/02-Bridge/index.md create mode 100644 docs/bridge/docs/02-Bridge/lp-tokens.svg create mode 100644 docs/bridge/docs/04-Routers/01-Synapse-Router/index.md create mode 100644 docs/bridge/docs/04-Routers/CCTP/index.md create mode 100644 docs/bridge/docs/04-Routers/RFQ/01-Relayer.md rename docs/bridge/docs/{rfq => 04-Routers/RFQ}/API/get-contract-addresses.api.mdx (100%) rename docs/bridge/docs/{rfq => 04-Routers/RFQ}/API/get-quotes.api.mdx (100%) rename docs/bridge/docs/{rfq/API/API.md => 04-Routers/RFQ/API/index.md} (98%) rename docs/bridge/docs/{rfq => 04-Routers/RFQ}/API/relay-ack.api.mdx (100%) rename docs/bridge/docs/{rfq => 04-Routers/RFQ}/API/upsert-quote.api.mdx (100%) rename docs/bridge/docs/{rfq => 04-Routers/RFQ}/API/upsert-quotes.api.mdx (100%) rename docs/bridge/docs/{rfq/Relayer => 04-Routers/RFQ}/dashboard.png (100%) create mode 100644 docs/bridge/docs/04-Routers/RFQ/index.md create mode 100644 docs/bridge/docs/05-Contracts/01-Synapse-Token.md create mode 100644 docs/bridge/docs/05-Contracts/02-Synapse-Router.md create mode 100644 docs/bridge/docs/05-Contracts/03-Intermediate-Tokens.md create mode 100644 docs/bridge/docs/05-Contracts/04-Liquidity-Pools.md create mode 100644 docs/bridge/docs/05-Contracts/05-CCTP.md create mode 100644 docs/bridge/docs/05-Contracts/06-RFQ.md create mode 100644 docs/bridge/docs/05-Contracts/07-Bridge-Zaps.md create mode 100644 docs/bridge/docs/05-Contracts/08-MiniChef.md rename docs/bridge/docs/{Services/Scribe.md => 06-Services/01-Scribe.md} (95%) rename docs/bridge/docs/{Services/Omnirpc.md => 06-Services/02-Omnirpc.md} (89%) rename docs/bridge/docs/{Services/Signer.md => 06-Services/03-Signer.md} (100%) rename docs/bridge/docs/{Services/Submitter.md => 06-Services/04-Submitter.md} (98%) rename docs/bridge/docs/{Observability.md => 06-Services/05-Observability.md} (99%) rename docs/bridge/docs/{Services => 06-Services}/img/signer/aws/create-acces-key.png (100%) rename docs/bridge/docs/{Services => 06-Services}/img/signer/aws/create-access-key.png (100%) rename docs/bridge/docs/{Services => 06-Services}/img/signer/aws/iam-dash.png (100%) rename docs/bridge/docs/{Services => 06-Services}/img/signer/aws/iam-preview-user.png (100%) rename docs/bridge/docs/{Services => 06-Services}/img/signer/aws/key-details.png (100%) rename docs/bridge/docs/{Services => 06-Services}/img/signer/aws/kms-1.png (100%) rename docs/bridge/docs/{Services => 06-Services}/img/signer/aws/kms-2.png (100%) rename docs/bridge/docs/{Services => 06-Services}/img/signer/aws/kms-advanced.png (100%) rename docs/bridge/docs/{Services => 06-Services}/img/signer/aws/kms-labels.png (100%) rename docs/bridge/docs/{Services => 06-Services}/img/signer/aws/kms-permissons.png (100%) rename docs/bridge/docs/{Services => 06-Services}/img/signer/aws/kms-user-permissions.png (100%) rename docs/bridge/docs/{Services => 06-Services}/img/signer/aws/kms-user.png (100%) rename docs/bridge/docs/{Services => 06-Services}/img/signer/aws/perms.png (100%) rename docs/bridge/docs/{Services => 06-Services}/img/signer/aws/review.png (100%) rename docs/bridge/docs/{Services => 06-Services}/img/signer/aws/user-list.png (100%) rename docs/bridge/docs/{Services => 06-Services}/img/signer/aws/user-perms.png (100%) rename docs/bridge/docs/{Services => 06-Services}/img/submitter/metrics.png (100%) create mode 100644 docs/bridge/docs/07-Support/Transaction-Support.md create mode 100644 docs/bridge/docs/07-Support/index.md delete mode 100644 docs/bridge/docs/CCTP/Contracts.md delete mode 100644 docs/bridge/docs/CCTP/Overview.md delete mode 100644 docs/bridge/docs/CCTP/Relayer.md delete mode 100644 docs/bridge/docs/CCTP/_category_.json delete mode 100644 docs/bridge/docs/rfq/API/sidebar.ts delete mode 100644 docs/bridge/docs/rfq/Contracts.md delete mode 100644 docs/bridge/docs/rfq/RFQ.md delete mode 100644 docs/bridge/docs/rfq/Relayer/Relayer.md delete mode 100644 docs/bridge/docs/rfq/_category_.json create mode 100644 docs/bridge/src/components/AnimatedLogo.tsx create mode 100644 docs/bridge/src/components/BridgeFlow.tsx create mode 100644 docs/bridge/src/components/CCTPFlow.tsx create mode 100644 docs/bridge/src/components/RFQFlow.tsx create mode 100644 docs/bridge/src/components/Routes.tsx create mode 100644 docs/bridge/src/components/SVGBridge.tsx create mode 100644 docs/bridge/src/components/SVGWidget.tsx create mode 100644 docs/bridge/src/components/USDC.tsx create mode 100644 docs/bridge/static/brand-assets/synapse-border-mark.svg create mode 100644 docs/bridge/static/brand-assets/synapse-brand-assets.zip create mode 100644 docs/bridge/static/brand-assets/synapse-logo-black.svg create mode 100644 docs/bridge/static/brand-assets/synapse-logo-onDark.svg create mode 100644 docs/bridge/static/brand-assets/synapse-logo-onLight.svg create mode 100644 docs/bridge/static/brand-assets/synapse-logo-white.svg create mode 100644 docs/bridge/static/brand-assets/synapse-mark-black.svg create mode 100644 docs/bridge/static/brand-assets/synapse-mark-white.svg create mode 100644 docs/bridge/static/brand-assets/synapse-mark.svg delete mode 100644 docs/bridge/static/img/docusaurus-social-card.jpg delete mode 100644 docs/bridge/static/img/docusaurus.png delete mode 100644 docs/bridge/static/img/favicon.ico delete mode 100644 docs/bridge/static/img/logo.svg delete mode 100644 docs/bridge/static/img/undraw_docusaurus_mountain.svg delete mode 100644 docs/bridge/static/img/undraw_docusaurus_react.svg delete mode 100644 docs/bridge/static/img/undraw_docusaurus_tree.svg diff --git a/docs/bridge/docs/01-About/01-DAO.md b/docs/bridge/docs/01-About/01-DAO.md new file mode 100644 index 0000000000..1f34cedf7c --- /dev/null +++ b/docs/bridge/docs/01-About/01-DAO.md @@ -0,0 +1,19 @@ +--- +title: Synapse DAO +--- + +# Synapse DAO + +Since launch of the Synapse Protocol, a number of governance initiatives have been put forward and voted on by the Synapse DAO. + +## Governance Model + +SYN holders with more than 100,000 SYN tokens can put forward proposals to be voted on by the DAO. SYN holders can vote using SYN on multiple chains, or delegate their votes to other token holders. + +In order to be adopted by the DAO, a proposal must gather at least `50% + 1` of all votes and reach the minimum quorum of 5 million SYN tokens. + +## Governance Components + +* **[Forum](https://forum.synapseprotocol.com/)**: A Platform to post proposals for discussion before an official vote +* **[Snapshot](https://snapshot.org/#/synapseprotocol.eth)**: A decentralized governance platform for conducting official votes. +* **[Discord](https://discord.gg/synapseprotocol)**: Discussion channel for general Synapse matters \ No newline at end of file diff --git a/docs/bridge/docs/01-About/02-Brand-Assets.md b/docs/bridge/docs/01-About/02-Brand-Assets.md new file mode 100644 index 0000000000..6ea765f2ca --- /dev/null +++ b/docs/bridge/docs/01-About/02-Brand-Assets.md @@ -0,0 +1,21 @@ +# Brand Assets + +### [synapse-brand-assets.zip](/brand-assets/synapse-brand-assets.zip) + +## Logo + +| Name | Image | +|----------------------------------------------------------------------|- +| [`synapse-logo-onLight.svg`](/brand-assets/synapse-logo-onLight.svg) | ![Synapse logo](/brand-assets/synapse-logo-onLight.svg) +| [`synapse-logo-onDark.svg`](/brand-assets/synapse-logo-onDark.svg) | ![Synapse logo](/brand-assets/synapse-logo-onDark.svg) +| [`synapse-logo-black.svg`](/brand-assets/synapse-logo-black.svg) | ![Synapse logo](/brand-assets/synapse-logo-black.svg) +| [`synapse-logo-white.svg`](/brand-assets/synapse-logo-white.svg) | ![Synapse logo](/brand-assets/synapse-logo-white.svg) + +## Mark + +| Name | Image | +|--------------------------------------------------------------------|- +| [`synapse-mark.svg`](/brand-assets/synapse-mark.svg) | ![Synapse logo](/brand-assets/synapse-mark.svg) +| [`synapse-mark-white.svg`](/brand-assets/synapse-mark-white.svg) | ![Synapse logo](/brand-assets/synapse-mark-white.svg) +| [`synapse-mark-black.svg`](/brand-assets/synapse-mark-black.svg) | ![Synapse logo](/brand-assets/synapse-mark-black.svg) +| [`synapse-border-mark.svg`](/brand-assets/synapse-border-mark.svg) | ![Synapse logo](/brand-assets/synapse-border-mark.svg) diff --git a/docs/bridge/docs/01-About/03-Routes.md b/docs/bridge/docs/01-About/03-Routes.md new file mode 100644 index 0000000000..5a6f844eb6 --- /dev/null +++ b/docs/bridge/docs/01-About/03-Routes.md @@ -0,0 +1,7 @@ +import Routes from '@site/src/components/Routes' + +# Chains & Tokens + +This page contains a list of supported tokens, listed per-chain. For a given pair, use the [Synapse Bridge](https://synapseprotocol.com) to see if a route between them exists. + + diff --git a/docs/bridge/docs/01-About/index.md b/docs/bridge/docs/01-About/index.md new file mode 100644 index 0000000000..a70db958b6 --- /dev/null +++ b/docs/bridge/docs/01-About/index.md @@ -0,0 +1,75 @@ +--- +title: About +--- + +import AnimatedLogo from '@site/src/components/AnimatedLogo' +import SVGBridge from '@site/src/components/SVGBridge' +import { BridgeFlow } from '@site/src/components/BridgeFlow' +import { CCTPFlow } from '@site/src/components/CCTPFlow' +import { RFQFlow } from '@site/src/components/RFQFlow' + + + +# Use Synapse + +Synapse is an Interchain Programming Interface. Developers read and write interchain data with Synapse, which has settled $50B in transactions between 2M+ users, and generated $30M+ in fees. + +Source: [Synapse Explorer analytics](https://explorer.synapseprotocol.com). + +## Interchain Bridge + +

+ +
Synapse Bridge
+
+ +* [Overview](/docs/Bridge) +* [Bridge guide](/docs/Bridge#how-to-bridge) + + +## Developers + +Embed or build a custom Bridge application. + +* **[SDK](/docs/Bridge/SDK)** – Call Synapse Router functions from your frontend or backend application. +* **[REST API](/docs/Bridge/REST-API)** – Endpoints and example code +* **[Widget](/docs/Bridge/Widget)** – Embed a customized Synapse Bridge in your application. + +## Synapse Routers + +Synapse Router automatically determines the appropriate router for each Bridge transaction. + +* **[Synapse Router](/docs/Routers/Synapse-Router)** – Returns and executes quotes for supported interchain transactions. +* **[CCTP](/docs/Routers/CCTP)** – Native router for USDC transactions. +* **[RFQ](/docs/Routers/RFQ)** – Relayers bid for the right to provide immediate delivery. + +
+
+ +
Synapse Router – Mint and burn any token between chains
+
+
+ +
CCTP – Use Circle contracts to mint and burn native USDC
+
+
+ +
RFQ – Take Immediate delivery from a destination relayer, who receives your origin chain assets on confirmation.
+
+
+ +## Community & Support + +Connect with other developers and the Synapse team + +* **[Discord](https://discord.gg/synapseprotocol)** +* **[Twitter](https://twitter.com/SynapseProtocol)** +* **[Telegram](https://t.me/synapseprotocol)** +* **[Forum](https://forum.synapseprotocol.com/)** + +## Additional Links + +Synapse transactions can be observed confirmed via the following methods: + +* **[Synapse Bridge](https://synapseprotocol.com)** – Bridge, Swap, and Stake via Synapse's cross-chain pools. +* **[Synapse Explorer](https://explorer.synapseprotocol.com)** – Public explorer for Synapse Bridge transactions. diff --git a/docs/bridge/docs/02-Bridge/01-SDK.md b/docs/bridge/docs/02-Bridge/01-SDK.md new file mode 100644 index 0000000000..c58d7e3f51 --- /dev/null +++ b/docs/bridge/docs/02-Bridge/01-SDK.md @@ -0,0 +1,222 @@ +--- +title: Bridge SDK +--- + +# Bridge SDK + +The Synapse Bridge SDK is the easiest way to integrate cross-chain token & liquidity transfers into your application. It is fully isomorphic and can be used on both client and server sides. + +The Synapse Bridge SDK is built on top of the [Synapse Router](/docs/Routers/Synapse-Router) contract. + +### Use cases + +* Integrate your front-end application with the Synapse Bridge. +* Provide bridge liquidity. +* Perform cross-chain arbitrage. +* Integrate the Synapse Javascript SDK with your non-Javascript application. + +## Install + +:::note requires Node v16+ + +The SDK has only been fully tested on Node 16+ or greater. Earlier versions are not guaranteed to work. + +::: + +Requires either the `npx` or `yarn` package manager. + +| Options +|- +| `npx install @synapsecns/sdk-router` +| `yarn install @synapsecns/sdk-router` + +## Configure Ethers + +The SDK package relies on the `@ethersproject` and `ethers` dependencies, installed from `npm`. + +To begin constructing bridge-related transactions, first set up your environment wiht your providers, and format them, along with the `chainIds` you will be using, to set up a `SynapseSDK` instance. + +#### Ethers v5 + +```js +//Set up providers (RPCs) for each chain desired +const arbitrumProvider: Provider = new ethers.providers.JsonRpcProvider( + 'https://arb1.arbitrum.io/rpc' +) +const avalancheProvider: Provider = new ethers.providers.JsonRpcProvider( + 'https://api.avax.network/ext/bc/C/rpc' +) + +//Structure arguments properly +const chainIds = [42161, 43114] +const providers = [arbitrumProvider, avalancheProvider] + +//Set up a SynapseSDK instance +const Synapse = new SynapseSDK(chainIds, providers) +``` + +#### Ethers v6 + +:::tip Ethers v6 + +Use of Ethers v6 requires the `@ethersproject/providers` dependency to be installed via `npm` or `yarn`: +* `npm install @ethersproject/providers@^5.7.2` +* `yarn add @ethersproject/providers@^5.7.2` + +::: + +```js +import { JsonRpcProvider } from '@ethersproject/providers' + +//Set up providers (RPCs) for each chain desired +const arbitrumProvider: Provider = new JsonRpcProvider( + 'https://arb1.arbitrum.io/rpc' +) +const avalancheProvider: Provider = new JsonRpcProvider( + 'https://api.avax.network/ext/bc/C/rpc' +) + +//Structure arguments properly +const chainIds = [42161, 43114] +const providers = [arbitrumProvider, avalancheProvider] + +//Set up a SynapseSDK instance +const Synapse = new SynapseSDK(chainIds, providers) +``` + +## Functions + +:::tip Query Data Type + +`originQuery` and `destQuery`, returned by `bridgeQuote()` and required for `bridge()`, are [`Query`](https://synapserouter.gitbook.io/untitled/) objects, which contain: + +* `swapAdapter`: (string): 0x address of the swap adapter. +* `tokenOut`: (string): 0x address of the outputted token on that chain. +* `minAmountOut`: (Ethers BigNumber): The min amount of value exiting the transaction. +* `deadline`: (Ethers BigNumber): The deadline for the potential transaction. +* `rawParams`: (string): 0x params for the potential transaction. + +::: + +### `bridgeQuote()` + +Get all relevant information regarding a possible transaction. + +#### Parameters + +`bridgeQuote()` requires the following arguments: + +* `fromChain` (number): Origin chain id. +* `toChain` (number): Destination chain id. +* `fromToken` (string): 0x token address on the origin chain. +* `toToken` (string): 0x token address on the destination chain. +* `amount` (Ethers BigNumber): The amount (with the correct amount of decimals specified by the token on the origin chain) +* `object` (three seperate args): +* `deadline` (Ethers BigNumber): Deadline for the transaction to be initiated on the origin chain, in seconds (optional) +* `originUserAddress` (string): Address of the user on the origin chain, optional, mandatory if a smart contract is going to initiate the bridge operation +* `excludedModules` (array): (optional) List of bridge modules to exclude from the result + +#### Return value + +`bridgeQuote` returns the following information + +* `feeAmount` (Ethers BigNumber): The calculated amount of fee to be taken. +* `bridgeFee` (number): The percentage of fee to be taken. +* `maxAmountOut` (Ethers BigNumber): The maximum output amount resulting from the bridge transaction. +* `originQuery` (`Query`): The query to be executed on the origin chain. +* `destQuery` (`Query`): The query to be executed on the destination chain. + +### `bridge()` + +Use `bridgeQuote` to request a Bridge transaction + +#### Parameters + +* `toAddress` (number): The 0x wallet address on the destination chain. +* `routerAddress` (string): The 0x contract address on the origin chain of the bridge router contract. +* `fromChain` (number): The origin chain id. +* `toChain` (number): The destination chain id. +* `fromToken` (string): The 0x token address on the origin chain. +* `amount` (Ethers BigNumber): The amount (with the correct amount of decimals specified by the token on the origin chain) +* `originQuery` (`Query`): The query to be executed on the origin chain. +* `destQuery` (`Query`): The query to be executed on the destination chain. + +#### Return value + +* `to` (string): 0x wallet address on the destination chain. +* `data` (string): Output data in 0x hex format + +### `allBridgeQuotes()` + +#### Return value + +Returns an array all possible bridge quotes, with the first item in the array being the cheapest route. + +Quotes are returned from various Bridge "types" such as [CCTP](/docs/Routers/CCTP) or [RFQ](/docs/Routers/RFQ). More information is available on the [Synapse Router](/docs/Routers/Synapse-Router) page, or [SynapseCNS](https://github.com/synapsecns/sdk-router) Github repository. + +## Examples + +### Get a quote + +```js +const quotes = await Synapse.bridgeQuote( + routerAddress + 42161, // Origin Chain + 43114, // Destination Chain + '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', // Origin Token Address + '0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664', // Destination Token Address + BigNumber.from('20000000') // Amount in + { + // Deadline for the transaction to be initiated on the origin chain, in seconds (optional) + deadline: 1234567890, + // List of bridge modules to exclude from the result, optional. + // Empty list means that all modules are included. + excludedModules: ['SynapseBridge', 'SynapseCCTP', 'SynapseRFQ'], + // Address of the user on the origin chain, optional. + // MANDATORY if a smart contract is going to initiate the bridge operation on behalf of the user. + originUserAddress: '0x1234567890abcdef1234567890abcdef12345678', + } +) +``` + +### Request a transaction + +```js +await Synapse.bridge( + '0x0AF91FA049A7e1894F480bFE5bBa20142C6c29a9', // To Address + bridgeQuote.routerAddress, // address of the contract to route the txn + 42161, // Origin Chain + 43114, // Destination Chain + '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', // Origin Token Address + BigNumber.from('20000000'), // Amount + quote.originQuery, // Origin query from bridgeQuote() + quote.destQuery // Destination query from bridgeQuote() +) +``` + +## Version 0.10.0 breaking changes + +### Options object + +* `deadline`, `excludeCCTP` (now `excludedModules`), and `originUserAddress` parameters are now found in an (optional) options object at the end of the arguments list for `bridgeQuote()`, and `allBridgeQuotes()`. +* `excludedModules` excludes one or more modules with an array of the module names. Supported names are `SynapseBridge`, `SynapseCCTP`, and `SynapseRFQ`. +* `originUserAddress` is required as part of the options object to initiate a bridge transaction on behalf of a user. + +### Examples +```js +bridgeQuote(...arguments, { + deadline: 1234567890, + excludedModules: ["SynapseCCTP"], + originUserAddress: "0x1234...", +}) + +allBridgeQuotes({ + deadline: 1234567890, + excludedModules: ["SynapseCCTP"], + originUserAddress: "0x1234...", +}) +``` + +### `FastBridgeRouter` + +The previous `FastBridgeRouter` deployment is deprecated, if your integration is using the hardcoded address, please see the router deployments/deprecated deployments table [here](https://github.com/synapsecns/sanguine/tree/master/packages/sdk-router#router-deployments) diff --git a/docs/bridge/docs/02-Bridge/02-REST-API.md b/docs/bridge/docs/02-Bridge/02-REST-API.md new file mode 100644 index 0000000000..8b46ddad60 --- /dev/null +++ b/docs/bridge/docs/02-Bridge/02-REST-API.md @@ -0,0 +1,205 @@ +--- +title: REST API +--- + +# REST API + +Get read-only data from on-chain Synapse contracts, and generate Bridge and Swap quotes, plus additional transaction information. + +## API-docs + +[`api.synapseprotocol.com/api-docs`](https://api.synapseprotocol.com/api-docs) + +## Previous versions + +| Date | Description +|------------------------|- +| 2024‑10‑01 | [https://synapse-rest-api-v2.herokuapp.com/](https://synapse-rest-api-v2.herokuapp.com/) is no longer maintained and has been fully deprecated as of October 2024. + +## Support + +Please read the documentation and examples carefully before reaching out on [Discord](https://discord.gg/synapseprotocol) for questions. + + + diff --git a/docs/bridge/docs/02-Bridge/03-Widget.md b/docs/bridge/docs/02-Bridge/03-Widget.md new file mode 100644 index 0000000000..eed13ea07e --- /dev/null +++ b/docs/bridge/docs/02-Bridge/03-Widget.md @@ -0,0 +1,196 @@ +--- +title: Widget +--- + +import SVGWidget from '@site/src/components/SVGWidget' + +# Bridge Widget + +The Synapse Widget lets you quickly and easily add bridging to their DeFi application, access specific tokens supported by the Synapse protocol, or even custom-build your own Synapse frontend. + +
+ +
Synapse Widget
+
+ +## Install + +Requires `React`, and the `npm` or `yarn` package manager. + +| Options | +|-| +| `npm install @synapsecns/widget` +| `yarn add @synapsecns/widget` + +## Quick start + +`import { Bridge } from @synapsecns/widget` into your app, and initialize it with your `web3Provider`. + +```jsx +import { Bridge } from '@synapsecns/widget' + +const MyApp = () => { + const web3Provider = new ethers.BrowserProvider(window.ethereum) + + return +} +``` + +This will result in a fully operational Bridge integrating the routes and tokens supported by the Synapse protocol. + +## Properties + +While the widget works out-of-the-box without any setup beyond `web3Provider`, configuration is recommended for reliability and performance. + +* `web3Provider` (required): Handles wallet connections. +* `customRpcs` (recommended): JSON-RPC endpoints. +* `targetChainIds`: List of destination chain IDs. Defaults to *all*. +* `targetTokens`: List of tokens to display. These tokens are imported from the widget package. Defaults to *all*. +* `customTheme`: Custom theme for the widget. Defaults to light mode. see [Theme](#theme) section for details. +* `container`: Includes a solid-background container if `true`. Defaults to `false`. +* `protocolName`: Short name by which to identify the protocol. Defaults to `"Target"`. +* `hideConsoleErrors`: Hide SDK and Widget `console.error` messages. Defaults to `false`. + +## web3Provider + +While the demo landing page uses the `ethers` library, any similar provider can be used. + +```jsx +// Ethers v5 +const web3Provider = new ethers.providers.Web3Provider(window.ethereum, 'any') + +// Ethers v6 +const web3Provider = new ethers.BrowserProvider(window.ethereum) +``` + +## customRpcs + +Set preferred RPC endpoints for each `chainId`. Defaults to Synapse fallback values for undefined chains. + +```jsx +import { Bridge, CustomRpcs } from '@synapsecns/widget' + +const customRpcs: CustomRpcs = { + 1: 'https://ethereum.my-custom-rpc.com', + 10: 'https://optimism.my-custom-rpc.com', + 42161: 'https://arbitrum.my-custom-rpc.com', +} + +const MyApp = () => { + const web3Provider = new ethers.BrowserProvider(window.ethereum) + + return +} +``` + +## targetChainIds & targetTokens + +Shows only the chains and tokens your project supports for onboarding, while still allowing users to onboard from, or bridge back to, any preferred chain or token. + +```jsx +import { Bridge, CustomRpcs, ETH, USDC, USDT } from '@synapsecns/widget' + +const MyApp = () => { + const web3Provider = new ethers.BrowserProvider(window.ethereum) + + return ( + + ) +} +``` + +:::tip Token names + +Token names must match the definitions in `src/constants/bridgeable.ts`. Metis USDC, for example, is `"METISUSDC"`. + +For chains, see `src/constants/chains.ts` as well. + +::: + +## Theme + +The widget will automatically generate a color palette from `bgColor` in the `customTheme` object, which you can also use to override individual color variables. + +:::tip Color modes + +If your application has multiple color modes, such as light and dark, reload the widget with the appropriate theme colors when your application’s theme changes. + +::: + +```jsx +const customTheme = { + // Generate from base color, 'dark', or 'light' + bgColor: '#08153a', + + // Basic customization + '--synapse-text': 'white', + '--synapse-secondary': '#ffffffb3', + '--synapse-root': '#16182e', + '--synapse-surface': 'linear-gradient(90deg, #1e223de6, #262b47e6)', + '--synapse-border': 'transparent', + + // Full customization (Uses 'basic' colors by default) + '--synapse-focus': 'var(--synapse-secondary)', + '--synapse-select-bg': 'var(--synapse-root)', + '--synapse-select-text': 'var(--synapse-text)', + '--synapse-select-border': 'var(--synapse-border)', + '--synapse-button-bg': 'var(--synapse-surface)', + '--synapse-button-text': 'var(--synapse-text)', + '--synapse-button-border': 'var(--synapse-border)', + + // Transaction progress colors (set bgColor to auto-generate) + '--synapse-progress': 'hsl(265deg 100% 65%)', + '--synapse-progress-flash': 'hsl(215deg 100% 65%)', + '--synapse-progress-success': 'hsl(120deg 100% 30%)', + '--synapse-progress-error': 'hsl(15deg 100% 65%)', +} + + +``` + +## Container + +The widget is full-width with a transparent background by default, but can supply its own solid background container. + +```jsx + +``` + +## useBridgeSelections hook + +An active list of dropdown selections are available from the `useBridgeSelections` React hook. + +```jsx +const { + originChain, + originToken, + destinationChain, + destinationToken, +} = useBridgeSelections() +``` + +### Structure + +* `Chain: { id: number, name: string }` +* `Token: { symbol: string, address: string }` + +## Example Apps + +For reference, you can find three example apps in the repository’s `/examples` folder. + +| `examples/` | Description +|-----------------|- +| `landing-page` | Functional demo with basic customization +| `with-react` | Simple React implementation +| `with-next` | Simple Next.js implementation + +## Support + +If you have questions or need help implementing the widget, reach out to the team on [Discord](https://discord.gg/synapseprotocol). diff --git a/docs/bridge/docs/02-Bridge/04-Code-Examples.md b/docs/bridge/docs/02-Bridge/04-Code-Examples.md new file mode 100644 index 0000000000..1816550110 --- /dev/null +++ b/docs/bridge/docs/02-Bridge/04-Code-Examples.md @@ -0,0 +1,350 @@ +--- +sidebar_label: Examples +--- + +# Example Code + +Example SDK & API implementations + +## Basic Implementation + +```js +// app.js + +import { JsonRpcProvider } from '@ethersproject/providers' +import { Provider } from '@ethersproject/abstract-provider' +import { SynapseSDK } from '@synapsecns/sdk-router' +import { BigNumber } from '@ethersproject/bignumber' + +//Set up providers (RPCs) for each chain desired** +const arbitrumProvider: Provider = new JsonRpcProvider('https://arb1.arbitrum.io/rpc') +const avalancheProvider: Provider = new JsonRpcProvider('https://api.avax.network/ext/bc/C/rpc') + +//Structure arguments properly +const chainIds = [42161, 43114] +const providers = [arbitrumProvider, avalancheProvider] + +//Set up a SynapseSDK instance +const Synapse = new SynapseSDK(chainIds, providers) + +// quote +const quote = await Synapse.bridgeQuote( + 42161, // From Chain + 43114, // To Chain + '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', // From Token + '0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664', // To Token + BigNumber.from('20000000'), // Amount + { + deadline: 1234567890, + excludedModules: ['SynapseRFQ'], + originUserAddress: '0x1234567890abcdef1234567890abcdef12345678', + } +) + +// bridge +await Synapse.bridge( + '0x0AF91FA049A7e1894F480bFE5bBa20142C6c29a9', // To Address + quote.routerAddress, // bridge router contract address + 42161, // Origin Chain + 43114, // Destination Chain + '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', // Origin Token Address + BigNumber.from('20000000'), // Amount + quote.originQuery, // Origin query from bridgeQuote() + quote.destQuery // Destination query from bridgeQuote() +) +``` + +## NextJS Implementation + +:::note Dependencies + +Wagmi, RainbowKit + +::: + +### App + +```tsx +// app.tsx + +import '@styles/global.css' +import '@rainbow-me/rainbowkit/styles.css' +import { SynapseProvider } from '@/utils/SynapseProvider' +import type { AppProps } from 'next/app' +import { Provider as EthersProvider } from '@ethersproject/abstract-provider' +import { JsonRpcProvider } from '@ethersproject/providers' +import { configureChains, createClient, WagmiConfig } from 'wagmi' +import { + mainnet, + arbitrum, + aurora, + avalanche, + bsc, + canto, + fantom, + harmonyOne, + metis, + moonbeam, + moonriver, + optimism, + polygon, +} from 'wagmi/chains' + +import { + getDefaultWallets, + RainbowKitProvider, + darkTheme, +} from '@rainbow-me/rainbowkit' +import { alchemyProvider } from 'wagmi/providers/alchemy' +import { publicProvider } from 'wagmi/providers/public' + +export default function App({ Component, pageProps }: AppProps) { + const { chains, provider } = configureChains([mainnet, + arbitrum, + aurora, + avalanche, + bsc, + canto, + fantom, + harmonyOne, + metis, + moonbeam, + moonriver, + optimism, + polygon], [ + alchemyProvider({ apiKey: 'API_KEY' }), + publicProvider(), + ]) + + const { connectors } = getDefaultWallets({ + appName: 'ExampleApp', + chains, + }) + + const wagmiClient = createClient({ + autoConnect: true, + connectors, + provider, + }) + + // Synapse client params setup example + let synapseProviders: EthersProvider[] = [] + chains.map((chain) => { + let rpc: EthersProvider = new JsonRpcProvider(chain.rpcUrls.default.http[0]) + synapseProviders.push(rpc) + }) + + return ( + + + chain.id)} + providers={synapseProviders} + > + + + + + ) +} +``` + +### Provider + +```tsx +// `@/utils/SynapseProvider.tsx` + +import { createContext, useContext } from 'react' +import { SynapseSDK } from '@synapsecns/sdk-router' +import { Provider } from '@ethersproject/abstract-provider' + +export const SynapseContext = createContext(null) + +export const SynapseProvider = ({ + children, + chainIds, + providers, +}: { + children: React.ReactNode + chainIds: number[] + providers: Provider[] +}) => { + const sdk = new SynapseSDK(chainIds, providers) + return ( + {children} + ) +} + +export const useSynapseContext = () => useContext(SynapseContext) +``` + +### Homepage + +```tsx +// `/homepage/index.tsx` + +import { useSynapseContext } from '@/utils/SynapseProvider' +import { Zero } from '@ethersproject/constants' +import { useState, useEffect } from 'react' +import { getNetwork } from '@wagmi/core' +import { + DEFAULT_FROM_CHAIN, + DEFAULT_TO_CHAIN, + DEFAULT_FROM_TOKEN, + DEFAULT_TO_TOKEN, +} from '@/constants/bridge' + +export default function HomePage({ address }: { address: `0x${string}` }) { + // Get the synapse sdk + const SynapseSDK = useSynapseContext() + + // Get the current time + const time = // add logic to get the current unix timestamp + // Example state hooks + const [fromToken, setFromToken] = useState(DEFAULT_FROM_TOKEN) + const [toToken, setToToken] = useState(DEFAULT_TO_TOKEN) + const [fromChainId, setFromChainId] = useState(DEFAULT_FROM_CHAIN) + const [toChainId, setToChainId] = useState(DEFAULT_TO_CHAIN) + const [amount, setAmount] = useState(Zero) + const [bridgeQuote, setBridgeQuote] = useState({ + outputAmountString: '', + quotes: { originQuery: null, destQuery: null }, + }) + + // Set connected network when component is mounted + useEffect(() => { + const { chain: fromChainIdRaw } = getNetwork() + setFromChainId(fromChainIdRaw ? fromChainIdRaw?.id : DEFAULT_FROM_CHAIN) + }, []) + + // Get Quote function + // Suggestion: this function should be triggered from an useEffect when amount or to/from token/chain is altered + const getQuote = async () = { + SynapseSDK.bridgeQuote( + fromChainId, + toChainId, + fromToken, + toToken, + amount, + { + deadline: time + 10000, + excludedModules: [], + originUserAddress: address, + } + ) + .then( + ({ feeAmount, bridgeFee, maxAmountOut, originQuery, destQuery }) => { + let toValueBigNum = maxAmountOut ?? Zero + let toValueBase = toValueBigNum.div(toDecimals).toString() + let toValueMantissa = toValueBigNum.mod(toDecimals).toString() + + setBridgeQuote({ + outputAmountString: toValueBase + '.' + toValueMantissa, + quotes: { + originQuery, + destQuery, + }, + }) + // do something + } + ) + .catch((err) => { + alert('error getting quote', err) + // do something + }) + + } + + // Execute bridge function + const executeBridge = async () = { + await Synapse.bridge( + toAddress, // To Address + bridgeQuote.routerAddress, // bridge router contract address + fromChainId, // Origin Chain + toChainId, // Destination Chain + fromToken, // Origin Token Address + amount, // Amount + bridgeQuote.quotes.originQuery, // Origin query from bridgeQuote() + bridgeQuote.quotes.destQuery // Destination query from bridgeQuote() + ).then(({to, data}) => { + // do something + } + ) + .catch((err) => { + alert('error bridging', err) + // do something + }) + } + +// ... + +} +``` + +## API Functions + +### Estimate bridge output + +```js +async function estimateBridgeOutput( + fromChain, + toChain, + fromToken, + toToken, + amountFrom +) { + const query_string = `fromChain=${fromChain}&toChain=${toChain}&fromToken=${fromToken}&toToken=${toToken}&amountFrom=${amountFrom}`; + const response = await fetch( + `https://api.synapseprotocol.com/bridge?${query_string}` + ); + + const response_json = await response.json(); + // Check if the response is an array and has at least one item + if (Array.isArray(response_json) && response_json.length > 0) { + return response_json[0]; // Return the first item + } else { + throw new Error('No bridge quotes available'); + } +} + +estimateBridgeOutput( + 1, // Ethereum + 42161, // Arbitrum + "USDC", + "USDC", + "1000" +).then(firstQuote => { + console.log('First bridge quote:', firstQuote); +}).catch(error => { + console.error('Error:', error.message); +}); +``` + +### Generate unsigned bridge transaction + +```js +async function generateUnsignedBridgeTxn( + fromChain, + toChain, + fromToken, + toToken, + amountFrom, + destAddress +) { + const query_string = `fromChain=${fromChain}&toChain=${toChain}&fromToken=${fromToken}&toToken=${toToken}&amount=${amountFrom}&destAddress=${addressTo}`; + const response = await fetch( + `https://api.synapseprotocol.com/bridgeTxInfo?${query_string}` + ); + const response_json = await response.json(); + return await response_json; +} + +generateUnsignedBridgeTxn( + 1, // Ethereum + 42161, // Arbitrum + "USDC", + "USDC", + "1000" + "0x2D2c027E0d1A899a1965910Dd272bcaE1cD03c22" +); +``` diff --git a/docs/bridge/docs/02-Bridge/index.md b/docs/bridge/docs/02-Bridge/index.md new file mode 100644 index 0000000000..dfa2ba2519 --- /dev/null +++ b/docs/bridge/docs/02-Bridge/index.md @@ -0,0 +1,58 @@ +--- +title: Bridge +--- + +import { BridgeFlow } from '@site/src/components/BridgeFlow' +import SVGBridge from '@site/src/components/SVGBridge' + +# Synapse Bridge + +The [Synapse Bridge](https://synapseprotocol.com) and [Solana Bridge](https://solana.synapseprotocol.com/) seamlessly swap on-chain assets between [20+ EVM and non-EVM blockchains](/docs/About/Routes) in a safe and secure manner. + +
+ +
+ +
User assets are sent to a bridge contract, moved to the `destChain`, and returned to the user.
+
+ +## Developers + +Add the [Custom Widget](#) to your DeFi application, or build your own DeFi applications using the [Synapse SDK](#). + +## Bridge Functions + +The [Synapse Router](#) will return an appropriate bridge function based on the chains and tokens involved. + +* **Canonical** – Assets are wrapped and then bridged. +* **[Liquidity Pools](#)** – Assets are swapped via Synapse liquidity pools. +* **[CCTP](#)** – Native router for USDC + +## Pool Liquidity + +![liquidity pool tokens](lp-tokens.svg)\ +Synapse liquidity pools use the nexus USD (nUSD) and nexus ETH (nETH) interchain stablecoins. nUSD and nETH are fully backed by the nexus USD and nexus ETH liquidity pools on Ethereum. + +When a token is bridged using a Synapse Liquidity Pool, it is converted to a nexus token on the source chain, which is then bridged to the destination chain, before being converted back into a native token. + +## How to Bridge + +
+ +
Synapse Bridge
+
+ +### Instructions + +1. Connect your wallet +2. Select your origin and destination chains from the dropdown menus +3. Select your origin token from the portfolio view, or dropdown menu +4. Enter the amount you wish to send +5. If you wish to send assets to a different wallet address, enable `Show withdrawal address` from the Settings menu (optional). +5. Connect your wallet to the origin chain, if necessary +6. Click `Bridge` to send a quote to your wallet for confirmation +7. Sign and Confirm the Bridge action from your wallet + +## Bridge Contracts + +Synapse Bridge contracts are available [here](https://docs.synapseprotocol.com/synapse-bridge/contract-addresses). diff --git a/docs/bridge/docs/02-Bridge/lp-tokens.svg b/docs/bridge/docs/02-Bridge/lp-tokens.svg new file mode 100644 index 0000000000..eec827e9ba --- /dev/null +++ b/docs/bridge/docs/02-Bridge/lp-tokens.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/bridge/docs/04-Routers/01-Synapse-Router/index.md b/docs/bridge/docs/04-Routers/01-Synapse-Router/index.md new file mode 100644 index 0000000000..7cf859b217 --- /dev/null +++ b/docs/bridge/docs/04-Routers/01-Synapse-Router/index.md @@ -0,0 +1,299 @@ +import { BridgeFlow } from '@site/src/components/BridgeFlow' + +# Synapse Router + +The Synapse Router abstracts much of the complexity around liquidity based bridging and Synapse Bridge contracts into a single [`bridge()`](/docs/Bridge/SDK/#bridge) function. + + + + + +
+ +
User assets are sent to a Bridge contract, moved to the `destChain`, and returned to the user.
+
+ +## Queries + +:::note Intermediate tokens + +Some Bridge transactions utilize an [intermediary token](/docs/Bridge/#pool-liquidity) (nETH, nUSD) the protocol has mint/burn permissions over. + +Swaps to/from an intermediate token return an empty query. + +::: + +Determining an optimal route with minimal slippage and maximum output is not trivial. Synapse Router condenses this complexity into a [`Query`](https://github.com/synapsecns/synapse-contracts/blob/7bc3a579c08838d7f4c3e62954135901abb16183/contracts/bridge/libraries/BridgeStructs.sol#L12-L18), or data structure of generic swap instructions that return a `token`, and an `amountReceived`. + + + +Given a bridgable token, a `Query` identifies on-chain “swaps” which allow the Bridge to identify the route(s) for an origin and intermediate token swap on the origin chain, and subsequent token swap on the destination chain. + +Origin and destination queries are taken from the `getOriginAmountOut()` and `getDestinationAmountOut()` supporting functions, which are called for each transaction, from which your application can decide its preferred route. + + + + +## Constructing a Bridge transaction + +:::tip Avoid manual construction + +Attempting to manually construct queries may result in transactions being reverted for misconfigured parameters. Always use the Router functions below to construct your Bridge transactions. + +::: + +**1. Get output `tokenSymbols`** + +Call `getConnectedBridgeTokens()` with your output token to receive a formatted `tokenSymbols` list. + +**2. Get `originQuery` list** + +Call `getOriginAmountOut()` with your input token, `tokenSymbols` list, and `amountIn`, to receive a `Query` list for the origin chain. + +**3. Get `destRequest` list** + +Convert each origin `Query` to a `destRequest` as seen in this example: + +```js +let requests = symbols.map((value, index) => { + let request: DestRequest = { + symbol: value, + amountIn: originQueries[index].minAmountOut, + }; + return request; +}); +``` + +**4. Get `destQuery` list** + +Call `getDestinationAmoutOut()` with your `destRequest` list and output token to receive a `Query` list for the destination chain. + +**5. select `originQuery` and `destQuery`** + +Determine which `originQuery` and `destQuery` to use. This simple example selects the origin and destination pair with the highest output: + +```js +let destQuery = maxBy(destQueries, (query) => query.minAmountOut); +let selectedIndex = destQueries.indexOf(destQuery) +let originQuery = originQueries[selectedIndex] +``` + +**6. Format queries and apply user settings** + +Add any user settings such as `slippage`, and `deadline` to your queries, and specify a [`swapAdapter`](#swap-adapter) for the swap to use. + +**7. `bridge()`** + +Call `bridge()` with your selected `originQuery` and `destQuery` pair. + +## Swap Adapter + +:::note + +SynapseRouterV1's swap adapter supports Synapse hosted pools. Future versions will allow additional adapters to support aggregators on different chains, allowing any-to-any Bridge transactions. + +::: + +The Synapse Adapter is a configurable wrapper that facilitates the "swap" action on the origin and destination chains, and exposes useful methods to get `Quote` and `Query` structs, supported pools, tokens, and more. + +## Example + +### Direct contract integration + +```js +/** + * Struct representing a bridge token. + * @param symbol Bridge token symbol: unique token ID consistent among all chains + * @param token Bridge token address + */ +type BridgeToken = { + symbol: String; + token: Address; +}; + +/** + * Struct representing a request for a swap quote from a bridge token. + * @param symbol Bridge token symbol: unique token ID consistent among all chains + * @param amountIn Amount of bridge token to start with, before the bridge fee is applied + */ +type DestRequest = { + symbol: String; + amountIn: BigInt; +}; + +/** + * Struct representing a request swap (list of instructions) for SynapseRouter. + * @param swapAdapter Adapter address that will perform the swap. Address(0) specifies a "no swap" query. + * @param tokenOut Token address to swap to. + * @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted. + * @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted. + * @param rawBytes ABI-encoded params for the swap that will be passed to `swapAdapter`. + */ +type SwapQuery = { + swapAdapter: Address; + tokenOut: Address; + minAmountOut: BigInt; + deadline: BigInt; + rawParams: BytesLike; +}; + +type UserSettings = { + maxSlippage: BigInt; + deadlineOrigin: BigInt; + deadlineDest: BigInt; +}; + +interface SynapseRouter { + /** + * Initiate a bridge transaction with an optional swap on both origin and destination chains + * @param to Address to receive tokens on destination chain + * @param chainId Destination chain id + * @param token Initial token for the bridge transaction to be pulled from the user + * @param amount Amount of the initial tokens for the bridge transaction + * @param originQuery Origin swap query. Empty struct indicates no swap is required + * @param destQuery Destination swap query. Empty struct indicates no swap is required + */ + bridge( + to: Address, + chainId: Number, + token: Address, + amount: BigInt, + originQuery: SwapQuery, + destQuery: SwapQuery + ): null; + + /** + * Gets the list of all bridge tokens (and their symbols), such that destination swap + * from a bridge token to `tokenOut` is possible. + * @param tokenOut Token address to swap to on destination chain + */ + getConnectedBridgeTokens(tokenOut: Address): BridgeToken[]; + + /** + * Finds the best path between `tokenIn` and every supported bridge token from the given list, + * treating the swap as "origin swap", without putting any restrictions on the swap. + * @param tokenIn Initial token that user wants to bridge/swap + * @param tokenSymbols List of symbols representing bridge tokens + * @param amountIn Amount of tokens user wants to bridge/swap + */ + getOriginAmountOut( + tokenIn: Address, + tokenSymbols: String[], + amountIn: BigInt + ): SwapQuery[]; + + /** + * Finds the best path between every supported bridge token from the given list and `tokenOut`, + * treating the swap as "destination swap", limiting possible actions to those available for every bridge token. + * Will take the bridge fee into account, when returning a quote for every bridge token. + * @param requests List of structs with following information: + * - symbol: unique token ID consistent among all chains + * - amountIn: amount of bridge token to start with, before the bridge fee is applied + * @param tokenOut Token user wants to receive on destination chain + */ + getDestinationAmountOut( + requests: DestRequest[], + tokenOut: Address + ): SwapQuery[]; +} + +/// Perform a cross-chain swap using Synapse:Bridge +/// Start from `amountIn` worth of `tokenIn` on origin chain +/// Receive `tokenOut` on destination chain +function synapseBridge( + originChainId: Number, + destChainId: Number, + tokenIn: Address, + tokenOut: Address, + amountIn: BigInt, + userOrigin: Address, + userDest: Address, + userSettings: UserSettings +) { + // Every cross-chain swap via Synapse:Bridge is fueled by using one of the + // supported "bridge tokens" as the intermediary token. + // A following set of actions will be initiated by a single SynapseRouter.bridge() call: + // - Origin chain: tokenIn -> bToken swap is performed + // - Synapse: bridge bToken from origin chain to destination + // - Destination chain: bToken -> tokenOut is performed + + // Here we describe a list of actions to perform such a cross-chain swap, knowing only + // - tokenIn, tokenOut, amountIn + // - SynapseRouter deployments + // - User settings for maximum slippage and deadline + // - User address on origin and destinaion chain (might be equal or different) + + // Beware: below is a TypeScript pseudocode. + + // 0. Fetch deployments of SynapseRouter on origin and destiantion chains + let routerOrigin = getSynapseRouter(originChainId); + let routerDest = getSynapseRouter(destChainId); + + // 1. Determine the set of bridge tokens that could enable "receive tokenOut on destination chain" + // For that we pefrorm a static call to SynapseRouter on destination chain + let bridgeTokens = routerDest.getConnectedBridgeTokens(tokenOut); + // Then we get the list of bridge token symbols + let symbols = bridgeTokens.map((token) => token.symbol); + + // 2. Get the list of Queries with possible swap instructions for origin chain + // For that we pefrorm a static call to SynapseRouter on origin chain + // This gets us the quotes from tokenIn to every bridge token (one quote per bridge token in the list) + let originQueries = routerOrigin.getOriginAmountOut( + tokenIn, + symbols, + amountIn + ); + + // 3. Get the list of Queries with possible swap instructions for destination chain + // First, we form a list of "destiantion requests" by merging + // list of token symbols with list of quotes obtained in step 2. + let requests = symbols.map((value, index) => { + let request: DestRequest = { + symbol: value, + amountIn: originQueries[index].minAmountOut, + }; + return request; + }); + // Then we perform a static call to SynapseRouter on destination chain + // This gets us the quotes from every bridge token to tokenOut (one quote per bridge token in the list) + // These quotes will take into account the fee for bridging the token to destination chain + let destQueries = routerDest.getDestinationAmountOut(requests, tokenOut); + + // 4. Pick a pair of originQueries[i], destQueries[i] to pefrom the cross-chain swap + // In this example we are picking the pair that yeilds the best overall quote + let destQuery = maxBy(destQueries, (query) => query.minAmountOut); + let selectedIndex = destQueries.indexOf(destQuery) + let originQuery = originQueries[selectedIndex] + + // Now we apply user slippage and deadline settings + originQuery = applyUserSettings(originQuery, userSettings) + destQuery = applyUserSettings(destQuery, userSettings) + + // 5. Call SynapseRouter on origin chain to perform a swap + let amountETH: BigInt; + // 0xEeee address is used to represent native ETH + if (tokenIn == "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE") { + // If user selected "native ETH" as tokenIn, we would need to modify msg.value for the call + amountETH = amountIn; + } else { + // If user selected an ERC-20 token as tokenIn, we would need to use msg.value=0 + amountETH = 0 + // We also need to check if user approved routerOrigin to spend `tokenIn` + if (allowance(tokenIn, userOrigin, routerOrigin) < amountIn) { + // Users needs to issue a token approval + // tokenIn.approve(routerOrigin, amountIn) + } + } + // Perform a call to Synapse Router with all the derevied parameters + // Use previously determined msg.value for this call + // (WETH wrapping is done by the Synapse Router) + routerOrigin.bridge{value: amountETH}( + userDest, + destChainId, + tokenIn, + amountIn, + originQuery, + destQuery + ); +} +``` diff --git a/docs/bridge/docs/04-Routers/CCTP/index.md b/docs/bridge/docs/04-Routers/CCTP/index.md new file mode 100644 index 0000000000..0f26283f2f --- /dev/null +++ b/docs/bridge/docs/04-Routers/CCTP/index.md @@ -0,0 +1,132 @@ +--- +sidebar_label: CCTP +--- + +import { CCTPFlow } from '@site/src/components/CCTPFlow' + +# CCTP Router + +A [Synapse Router](../Synapse-Router) bridge module which uses Circle's [Cross-Chain Transfer Protocol](https://www.circle.com/en/cross-chain-transfer-protocol) to natively mint & burn USDC. + +
+ +
User assets are sent to a Circle contract, moved to the `destChain`, and returned to the user.
+
+ +## Architecture + +### Contracts + +[Synapse CCTP contracts](/docs/Contracts/CCTP) overlay Circle CCTP contracts to mint and burn USDC and fulfill CCTP transactions. + +### Configuration +CCTP can be configured to bridge through any supported liquidity source, such as [Uniswap](https://github.com/synapsecns/synapse-contracts/blob/master/contracts/router/modules/pool/uniswap/UniswapV3Module.sol), [Curve](https://github.com/synapsecns/synapse-contracts/blob/master/contracts/router/modules/pool/curve/CurveV1Module.sol), [Algebra](https://github.com/synapsecns/synapse-contracts/blob/master/contracts/router/modules/pool/algebra/AlgebraModule.sol), [DAI PSM](https://github.com/synapsecns/synapse-contracts/blob/master/contracts/router/modules/pool/dss/DssPsmModule.sol), and others. + +### Relayer + +CCTP Relayers allow anyone to coordinate on-chain events and stored message states to send native USDC through SynapseCCTP and Circle's CCTP contracts. + +:::tip + +While the Synapse CCTP Golang relayer can be run by anyone, and is easily observable, you can also build and run your own relayer permissionlessly in any programming language. + +::: + +## Behavior + +CCTP Relayers poll for new transactions and state updates from CCTP contracts on-chain, to store in an off-chain database. + +Attestations from the [Circle API](https://developers.circle.com/stablecoin/reference) are submitted to the destination contract, and marked `Complete` when a transaction receipt is received. + +| State | Description | +|-------------|-------------| +| `Pending` | Initiated on origin chain, and pending attestation | +| `Attested` | Waiting for submission on destination chain | +| `Submitted` | Confirmed on destination chain | +| `Complete` | Completed on destination chain | + + + + +## Configure + +CCTP Relayers require a YAML configuration file path to be provided at run time. + +:::note cctp_type + +* **`synapse`** (recommended): Uses events & metadata from [Synapse CCTP contracts](/docs/Contracts/CCTP), and `synapse_cctp_address` when configuring `chains`. + +* **`circle`** (USDC to USDC only): Uses raw [TokenMessenger](https://github.com/circlefin/evm-cctp-contracts/blob/817397db0a12963accc08ff86065491577bbc0e5/src/TokenMessenger.sol) events, and `token_messenger_address` when configuring `chains`. + +::: + +### Parameters + +* `cctp_type`: Determines which event types and contracts are used. +* `chains`: `chain_id` list +* `base_omnirpc_url`: [OmniRPC service](/docs/Services/Omnirpc) base URL +* `unbonded_signer`: [Signer service](/docs/Services/Signer) — *should be a mounted secret* +* `port`: Relayer port (e.g. 8080) +* `host`: Relayer host (e.g. localhost) — *do not publicly expose*. +* `http_backoff_initial_interval_ms`: Initial backoff interval in milliseconds. +* `retry_interval_ms`: Retry interval between attestation requests in milliseconds — *[CCTP API Rate Limit](https://developers.circle.com/stablecoins/docs/limits)*. + + ### Example + +```yaml +cctp_type: "synapse" +# prod contract addresses +chains: + - chain_id: 1 + synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" + - chain_id: 42161 + synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" +base_omnirpc_url: "http://omnrpc-url/" +unbonded_signer: + type: "AWS" + # should be a mounted secret + file: "/config/aws.txt" +http_backoff_initial_interval_ms: 1000 +http_backoff_max_elapsed_time_ms: 300000 +# submitter config for cctp +submitter_config: + chains: + 1: + supports_eip_1559: true + gas_estimate: 1000000 + 42161: + gas_estimate: 30000000 + max_gas_price: 10000000000 + supports_eip_1559: true +``` + + +## Run + +### From Docker + +Run the Docker [image](https://github.com/synapsecns/sanguine/pkgs/container/sanguine%2Fcctp-relayer) along with the path to your [YAML configuration file](#configure). + +1. `docker run ghcr.io/synapsecns/sanguine/cctp-relayer:latest --config /path/to/config.yaml` + +### From Source + +:::note Requires Go 1.21 or higher + +Not generally recommended for end-users. + +::: + +Clone the Sanguine repository, then run the main.go file along with the path to your [YAML configuration file](#configure). + +1. `git clone https://github.com/synapsecns/sanguine --recursive` +2. `cd sanguine/services/cctp-relayer` +3. `go run main.go --config /path/to/config.yaml` + +### With Helm + +There is a helm chart available for the CCTP Relayer [here](https://artifacthub.io/packages/helm/synapse/cctp/0.2.0), but it is recommended you create your own. + +### Recommended services + +CCTP Relayer uses open telemetry for tracing and metrics. See the [Observability](/docs/Services/Observability) page for details. We highly recommend setting up the [Submitter Dashboard](/docs/Services/Submitter) as well. diff --git a/docs/bridge/docs/04-Routers/RFQ/01-Relayer.md b/docs/bridge/docs/04-Routers/RFQ/01-Relayer.md new file mode 100644 index 0000000000..e2f87823b5 --- /dev/null +++ b/docs/bridge/docs/04-Routers/RFQ/01-Relayer.md @@ -0,0 +1,311 @@ +--- +sidebar_position: 0 +sidebar_label: Relayer +--- + +# RFQ Relayer + +:::note + +Relayers must be whitelisted in order to fulfill bridge requests. + +::: + +A Go application which coordinates on-chain events and stored message states to relay user funds. Relayers are easily observable. + +The canonical RFQ Relayer handles **three event loops**: quoting routes, approving relays, and rebalancing funds. + +## Quote + +Continuously track and update route quotes based on changes to available and in-flight balances via [API](API). The quote should update each time the available balance or other parameters change. + +| Param | Description +|---------|- +| Balance | Maximum amount the relayer can fill +| Offset | Token price percentage, used to ensure the relayer is profitable. +| Fee | `fixed_fee_multiplier` (from [config](#configure)) multiplied by `origin_gas_estimate + destination_gas_estimate` + + + +## Relay + +Listen to on-chain events and database updates to move [`BridgeRequest`](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgeparams) objects through the following states: + +| State | Description +|----------------------|- +| `Seen` | [`BridgeRequested`](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgerequested) event stored in the db. +| `WillNotProcess` | [`BridgeRequested`](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgerequested) event is invalid +| `NotEnoughInventory` | Retry later +| `CommittedPending` | All checks pass, waiting for transaction to finalize +| `CommittedConfirmed` | Transaction is finalized on-chain +| `RelayPending` | Called [`relay`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#relay) +| `RelayComplete` | Relay completed on-chain +| `ProvePosting` | Called [`prove`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#prove) +| `ClaimPending` | Called [`claim`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#claim) +| `ClaimComplete` | Dispute period expired, funds received. + +## Rebalance + +For cases where flows are mono-directional, the Relayer provides an interface for rebalancing funds. + +Automated rebalancing is configurable by token and currently supports CCTP routes as well as native bridging on Scroll. Rebalancing evaluation follows this logic every `rebalance_interval`: + +For each supported rebalance method, calculate the `total_balance` of each token across the chains that support that rebalance method, and which tokens have a balance below `total_balance * maintenance_balance_pct`. + +:::note + +To limit in-flight inventory, only one rebalance can be pending at a time for each token. We select the 'best' rebalance candidate as the rebalance with the largest delta between origin and destination balance. + +::: + +The rebalance amount is the smaller of the maximum rebalance amount on origin, and minimum rebalance amount on destination, clamped to the `min` and `max` values in the configuration file. + +#### Example + +| # | CCTP | USDC | `maintenence_pct` | `initial_pct` +|---|------|------|-------------------|- +| 1 | Yes | 20% | 40% | 100 USDC +| 2 | Yes | 20% | 40% | 900 USDC +| 3 | No | 20% | 40% | 2000 USDC + +The total balance of CCTP-supported chains is `1000 USDC`. Since chain 1, with 10% of the total inventory, is below the 20% maintenance threshold, the system triggers a rebalance from Chain 2 to Chain 1. + +Chain 3 does not support CCTP, and is not considered for rebalance. + +The maximum rebalance amount is `600 USDC` which takes chain 2 to its 40% initial threshold + +`300 USDC` is sent from chain 2 to chain 1 is, which is the minimum required to reach chain 1's initial 40% threshold. + +:::note Scroll + +Rebalancing for certain native bridges (e.g Scroll) is supported. It works slightly differently as flows are only supported between Scroll and Mainnet. + +At a high level, the rebalancer checks inventory on Scroll versus other chains, and if imbalanced, initiates a bridge to mainnet, allowing the CCTP relayer to rebalance funds where needed. + +::: + +## Configure + +RFQ Relayer requires a YAML configuration file path to be provided at run time. + + + +:::note rebalance_method + +`synapse` collects a fee, but does not spend user gas. Use `synapse_cctp_address` when configuring `chains`. + +`cctp` has no fees, but spends user gas. Use `token_messenger_address` when configuring `chains`. + +::: + +* `submitter_config`: Covered [here](/docs/Services/Submitter#observability). Controls gas parameters for on-chain transactions. +* `database`: Database settings for API backend. Required to store quotes and other information. SQLite with `DSN` set to a `/tmp/` directory is recommended for development. + * `type`: Database driver to use, can be `mysql` or `sqlite`. + * `dsn`: 'Data Source Name'. If using SQLite, this can be a path. For MySQL see [here](https://dev.mysql.com/doc/connector-odbc/en/connector-odbc-configuration.html) for more information. +* `screener_api_url` (optional): [Screener API](https://github.com/synapsecns/sanguine/tree/master/contrib/screener-api#screening-api) +* `rfq_url`: [Mainnet & Testnet addresses](API/#api-urls) +* `omnirpc_url`: [Running an OmniRPC instance](/docs/Services/Omnirpc) +* `rebalance_interval`: How often to rebalance. Can use `s` (seconds), `m` (minutes), or `h` (hours) +* `relayer_api_port`: Controls the relayer. Should be private or secured. +* `base_chain_config`: Default chain configuration. Overridden by individual chain configurations. See `chains` for details. +* `enable_guard`: Run a guard in the same instance. +* `submit_single_quotes`: Whether to use the batch endpoint to post quotes to the API. Useful for debugging. +* `chains`: Individual configurations to override `base_chain_config`. + * `rfq_address`: [RFQ contract address](/docs/Contracts/RFQ) for this chain. + * `confirmations`: how many confirmations to wait before acting on an event. This will vary per-chain. + * `tokens`: this is a map of token symbol→token info for this chain. For example, token may be USDC, ETH, etc + * `address`: address of the token on this chain id + * `decimals`: number of decimals this token uses. Please verify this against the token contract itself. + * `min_quote_amount`: smallest amount to quote for a given chain. This should be balanced against expected gas spend for a relayer to be profitable. `min_quote_amount` is to be given in decimal units (so 1000.00 is 1000) + * `rebalance_method`: Some tokens may not have a rebalance method. Uses `synapse` or `cctp`. + * `maintenance_balance_pct`: Portion of liquidity to maintain for this token on this chain. A balance under this amount triggers a rebalance. + * `initial_balance_pct`: Portion of liquidity to maintain after a rebalance. Should total 100% across all chains. + * `min_rebalance_amount`: Minimum amount of this token to try to rebalance. + * `max_rebalance_amount`: Maximum amount of this token to rebalance at one time. + + * `synapse_cctp_address` (optional): Used with `rebalance_method: synapse`. Uses a [Synapse CCTP address](/docs/Contracts/CCTP). + * `token_messenger_address` (optional): Used with `rebalance_method: cctp`. Tells the relayer to use the token messenger instead of Synapse. +* `quotable_tokens`: list of `[chain-id]-[token_address]: [chain-id]-[token_address]`. For example 1-0x00… could be paired with 10-0x01 + ```yaml + "1-0x00": + - "1-0x01" + ``` +* `cctp_relayer_config`: See [CCTP](/docs/Routers/CCTP). + +### Example + +
+ `config.yml` + ```yaml +submitter_config: # please see the more detailed submitter documentation + chains: + 1: + supports_eip_1559: true + gas_estimate: 1000000 +database: + type: sqlite # can be other mysql or sqlite + dsn: /tmp/db # should be the dsn of your database. If using sqlite, this can be a path + +signer: # please see more detailed signer config #can be text, gcp, or aws + type: GCP + file: /config/signer.txt + +screener_api_url: 'http://screener-url' # can be left blank +rfq_url: 'http://rfq-api' # url of the rfq api backend. +omnirpc_url: 'http://omnirpc' # url of the omnirpc instance, please reference the Omnirpc section under Services for proper configuration +rebalance_interval: 2m # how often to rebalance +relayer_api_port: '8081' # api port for the relayer api +volume_limit: 10000 # USD price cap for a bridge under block confirmation minimum (configurable under `chains`) + +base_chain_config: # this is hte base chain config, other chains override it + confirmations: 0 + # Claim (72.5k) + Prove (57.5k) gas limits, rounded up + origin_gas_estimate: 130_000 + # Relay gas limit, rounded up + dest_gas_estimate: 110_000 + quote_offset_bps: 2 + native_token: ETH + quote_pct: 90 + min_gas_token: 1000000000000000000 + fixed_fee_multiplier: 1.25 + +chains: + 1: + rfq_address: "0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E" # rfq contract address on eth + synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" # ccctp contract address on eth + token_messenger_address: "0xbd3fa81b58ba92a82136038b25adec7066af3155" # token messenger address on eth, note: only one of token_messenger_address or synapse_cctp_address actually needs to be present + cctp_start_block: 19341000 + confirmations: 2 + tokens: + USDC: + address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" + decimals: 6 + price_usd: 1.0 + min_quote_amount: 10000 + rebalance_method: "circlecctp" + maintenance_balance_pct: 20 + initial_balance_pct: 50 + max_rebalance_amount: 500000 + ETH: + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" + decimals: 18 + price_usd: 2600 + 10: + rfq_address: "0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E" + synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" + token_messenger_address: "0x2B4069517957735bE00ceE0fadAE88a26365528f" + cctp_start_block: 116855000 + l1_fee_chain_id: 1 + # Prove + Claim L1 gas estimate + l1_fee_origin_gas_estimate: 20 + # Relay L1 gas estimate + l1_fee_dest_gas_estimate: 10 + tokens: + USDC: + address: "0x0b2c639c533813f4aa9d7837caf62653d097ff85" + decimals: 6 + price_usd: 1.0 + min_quote_amount: 10000 + rebalance_method: "circlecctp" + maintenance_balance_pct: 20 + initial_balance_pct: 50 + max_rebalance_amount: 500000 + ETH: + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" + decimals: 18 + price_usd: 2600 + +quotable_tokens: + 10-0x0b2c639c533813f4aa9d7837caf62653d097ff85: + - "1-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" + 1-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48: + - "10-0x0b2c639c533813f4aa9d7837caf62653d097ff85" + 1-0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE: + - "10-0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" + 10-0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE: + - "1-0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" + +fee_pricer: + gas_price_cache_ttl: 60 + token_price_cache_ttl: 60 + +cctp_relayer_config: + cctp_type: "circle" + circle_api_url: "https://iris-api.circle.com/v1/attestations" + chains: + - chain_id: 1 + synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" + token_messenger_address: "0xbd3fa81b58ba92a82136038b25adec7066af3155" + - chain_id: 10 + synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" + token_messenger_address: "0x2B4069517957735bE00ceE0fadAE88a26365528f" + base_omnirpc_url: "http://omnirpc" # Make sure this is configured properly + unbonded_signer: + type: GCP + file: /config/signer.txt + http_backoff_initial_interval_ms: 1000 + http_backoff_max_elapsed_time_ms: 300000 + ``` +
+ +## Run + +### From Docker + +Run the Docker [image](https://github.com/synapsecns/sanguine/pkgs/container/sanguine%2Frfq-relayer) along with the path to your [YAML configuration file](#configure). + +1. `docker run ghcr.io/synapsecns/sanguine/rfq-relayer:latest --config /path/to/config` + +### From Source + +:::note Requires Go 1.21 or higher + +Not generally recommended for end-users. + +::: + +Clone the Sanguine repository, then run the main.go file along with the path to your [YAML configuration file](#configure). + +1. `git clone https://github.com/synapsecns/sanguine --recursive` +2. `cd sanguine/services/rfq/relayer` +3. `go run main.go --config /path/to/config.yaml` + +## sendChainGas + +Boolean flag available to Bridge users. When `sendChainGas` is `true`, the amount to send is specified as `chainGasAmount` in the FastBridge contract on the destination chain. + +:::note + +`chainGasAmount` is currently set to `0` on all contracts. You can ignore `sendChainGas` by only providing quotes where `sendChainGas` is not set, or `chainGasAmount` is `0`. + +::: + +## Withdrawals + +The `POST /withdraw` endpoint is exposed to allow withdrawals from the relayer wallet without needing to deal with the private key directly. This can be used for manual rebalancing. To use this feature, set the following config values: + +```yaml +enable_api_withdrawals: true +withdrawal_whitelist: + - +``` + +The relayer CLI (at `services/rfq/relayer/main.go`) exposes a withdrawal command for convenience: + +```bash +go run main.go withdraw --relayer-url https://localhost:8081 --chain-id 1 --amount 1000000000000000000 --token-address 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE --to 0x0000000000000000000000000000000000000000 +``` + +Be sure to sub in your respective `to` address! + +## Observability + +The RFQ relayer implements open telemetry for both tracing and metrics. Please see the [Observability](/docs/Services/Observability) page for more info. There is also a custom [grafana dashboard](https://github.com/synapsecns/sanguine/tree/master/services/rfq/relayer/dashboards/dashboard.json) available for the relayer. We'd also highly recommend setting up the [submitter dashboard](/docs/Services/Submitter) as well. + +![Relayer Grafana Dashboard](dashboard.png) + +The metrics exposed by the relayer are: + +- `inventory_balance`: The balance of the inventory on the chain for a given `token_name` and `relayer`. +- `quote_amount`: The amount quoted for a given `token_name` and `relayer`. +- `status_count`: The distribution of non-terminal `QuoteRequestStatus` values over time. diff --git a/docs/bridge/docs/rfq/API/get-contract-addresses.api.mdx b/docs/bridge/docs/04-Routers/RFQ/API/get-contract-addresses.api.mdx similarity index 100% rename from docs/bridge/docs/rfq/API/get-contract-addresses.api.mdx rename to docs/bridge/docs/04-Routers/RFQ/API/get-contract-addresses.api.mdx diff --git a/docs/bridge/docs/rfq/API/get-quotes.api.mdx b/docs/bridge/docs/04-Routers/RFQ/API/get-quotes.api.mdx similarity index 100% rename from docs/bridge/docs/rfq/API/get-quotes.api.mdx rename to docs/bridge/docs/04-Routers/RFQ/API/get-quotes.api.mdx diff --git a/docs/bridge/docs/rfq/API/API.md b/docs/bridge/docs/04-Routers/RFQ/API/index.md similarity index 98% rename from docs/bridge/docs/rfq/API/API.md rename to docs/bridge/docs/04-Routers/RFQ/API/index.md index dfbc77ffe3..6946022d02 100644 --- a/docs/bridge/docs/rfq/API/API.md +++ b/docs/bridge/docs/04-Routers/RFQ/API/index.md @@ -3,6 +3,8 @@ sidebar_position: 0 sidebar_label: API --- +# RFQ API + :::note This guide is mostly meant for developers who are working on building their own quoter or frontend for rfq. If you are just looking to run a relayer, please see [Relayer](../Relayer). If you are looking to integrate rfq, please see the API docs below the dropdown. @@ -48,8 +50,8 @@ The RFQ API expects the signatures to have V values as 0/1 rather than 27/28. Th ### API Urls - - mainnet: rfq-api.omnirpc.io - - testnet: rfq-api-testnet.omnirpc.io + - Mainnet: `rfq-api.omnirpc.io` + - Testnet: `rfq-api-testnet.omnirpc.io` ## Running the API: @@ -72,7 +74,7 @@ Yaml settings: - `database` - The database settings for the API backend. A database is required to store quotes and other information. Using SQLite with a dsn set to a `/tmp/` directory is recommended for development. - `type` - the database driver to use, can be `mysql` or `sqlite`. - `dsn` - the dsn of your database. If using sqlite, this can be a path, if using mysql please see [here](https://dev.mysql.com/doc/connector-odbc/en/connector-odbc-configuration.html) for more information. - - `omnirpc_url` - The omnirpc url to use for querying chain data (no trailing slash). For more information on omnirpc, see [here](../../Services/Omnirpc.md). + - `omnirpc_url` - The omnirpc url to use for querying chain data (no trailing slash). For more information on omnirpc, see [here](/docs/Services/Omnirpc). - `bridges` - A key value map of chain id to FastBridge contract address. The API will only allow quotes to be posted on these chains. - `port` - The port to run the http server on. diff --git a/docs/bridge/docs/rfq/API/relay-ack.api.mdx b/docs/bridge/docs/04-Routers/RFQ/API/relay-ack.api.mdx similarity index 100% rename from docs/bridge/docs/rfq/API/relay-ack.api.mdx rename to docs/bridge/docs/04-Routers/RFQ/API/relay-ack.api.mdx diff --git a/docs/bridge/docs/rfq/API/upsert-quote.api.mdx b/docs/bridge/docs/04-Routers/RFQ/API/upsert-quote.api.mdx similarity index 100% rename from docs/bridge/docs/rfq/API/upsert-quote.api.mdx rename to docs/bridge/docs/04-Routers/RFQ/API/upsert-quote.api.mdx diff --git a/docs/bridge/docs/rfq/API/upsert-quotes.api.mdx b/docs/bridge/docs/04-Routers/RFQ/API/upsert-quotes.api.mdx similarity index 100% rename from docs/bridge/docs/rfq/API/upsert-quotes.api.mdx rename to docs/bridge/docs/04-Routers/RFQ/API/upsert-quotes.api.mdx diff --git a/docs/bridge/docs/rfq/Relayer/dashboard.png b/docs/bridge/docs/04-Routers/RFQ/dashboard.png similarity index 100% rename from docs/bridge/docs/rfq/Relayer/dashboard.png rename to docs/bridge/docs/04-Routers/RFQ/dashboard.png diff --git a/docs/bridge/docs/04-Routers/RFQ/index.md b/docs/bridge/docs/04-Routers/RFQ/index.md new file mode 100644 index 0000000000..b44418cde1 --- /dev/null +++ b/docs/bridge/docs/04-Routers/RFQ/index.md @@ -0,0 +1,77 @@ +--- +sidebar_label: RFQ +--- + +import { RFQFlow } from '@site/src/components/RFQFlow' + +# RFQ Router + +A [Synapse Router](../Synapse-Router) bridge module which matches on-chain user requests against bridge quotes posted by decentralized [Relayers](Relayer). + +
+ +
User assets are sent to a Bridge contract, and held until a Solver executes their quote on the `destChain`.
+
+ +## Architecture + +[Synapse Fast Bridge contracts](/docs/Contracts/RFQ) coordinate decentralized Solvers to match user requests against the best quote for a given route, and secure user funds while their transaction is fulfilled. + + + +| Agents | Description +|---------|- +| Quoters | Quote distribution services run through traditional [APIs](API), or protocols like libp2p, irc, or dht. +| Solvers | Posts, then fulfills, route quotes through a [Relayer](Relayer), when matched by the Fast Bridge contract against a user request. +| Users | Uses a route quote to form a bridge request which is matched on-chain by the solver who posted the quote. +| Guards | Raises a dispute if errors or fraudulent activity are detected. + +## Behavior + +After receiving a [`BridgeRequest`](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgeparams) (broadcast as a [`BridgeRequested`](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgerequested) event), a Solver executes the transaction by calling [`relay`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#relay) on the Bridge contract. + +The Bridge relays the requested funds ([`msg.value`](https://ethereum.stackexchange.com/questions/43362/what-is-msg-value) in the case of ETH) from Solver to User, allowing the Solver that accepted the bridge to call [`prove`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#prove) on the Bridge contract, and receive their funds at the end of the optimistic period + +| `#` | State | Description +|-----|-------------|- +| `0` | `Null` | Bridge transaction does not exist yet on origin chain +| `1` | `Requested` | [`BridgeRequested`](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgerequested) event broadcast. Waiting for Relayer +| `2` | `Proved` | Relayer called [`relay`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#relay), and [`prove`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#prove), and is waiting for the optimistic period to end. +| `3` | `Claimed` | Relayer called [`claim`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#claim) and received their funds. +| `4` | `Refunded` | Relayer did not claim within the optimistic period, or a dispute was decided in favor of the user. + + + + + + + + + + + + + + + + + +## Dispute Period and Guards + +The RFQ system includes an optimistic dispute window in which Guard contracts may initiate a dispute if they detect errors or fraudulent activity, such as incorrect fill amounts or proofs submitted by the wrong relayer. + +In a successful dispute, the relayer loses their claimable funds. This design is intended to enforce honest behavior while also protecting honest relayers in cases of blockchain reorgs. + +## Unfulfilled requests + +If a request is not fulfilled, users can reclaim their funds by using the [`claim`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#claim) function once the optimistic window has passed. diff --git a/docs/bridge/docs/05-Contracts/01-Synapse-Token.md b/docs/bridge/docs/05-Contracts/01-Synapse-Token.md new file mode 100644 index 0000000000..e839a6f865 --- /dev/null +++ b/docs/bridge/docs/05-Contracts/01-Synapse-Token.md @@ -0,0 +1,33 @@ +--- +title: Synapse Token +--- + +:::note This list may be incomplete + +The canonical list is hosted within the SynapseCNS on [Github](https://github.com/synapsecns/synapse-contracts). + +::: + +# Synapse Token + +| Chain | Address | +|-----------|----------------------------------------------| +| Arbitrum | `0x080f6aed32fc474dd5717105dba5ea57268f46eb` [↗](https://arbiscan.io/token/0x080f6aed32fc474dd5717105dba5ea57268f46eb)| +| Aurora | `0xd80d8688b02B3FD3afb81cDb124F188BB5aD0445` [↗](https://explorer.mainnet.aurora.dev/address/0xd80d8688b02B3FD3afb81cDb124F188BB5aD0445/transactions)| +| Avalanche | `0x1f1E7c893855525b303f99bDF5c3c05Be09ca251` [↗](https://snowtrace.io/address/0x1f1E7c893855525b303f99bDF5c3c05Be09ca251)| +| Base | `0x432036208d2717394d2614d6697c46DF3Ed69540` [↗](https://basescan.org/address/0x432036208d2717394d2614d6697c46DF3Ed69540)| +| Blast | `0x9592f08387134e218327E6E8423400eb845EdE0E` [↗](https://blastscan.io/address/0x9592f08387134e218327E6E8423400eb845EdE0E)| +| Boba | `0xb554A55358fF0382Fb21F0a478C3546d1106Be8c` [↗](https://blockexplorer.boba.network/tokens/0xb554A55358fF0382Fb21F0a478C3546d1106Be8c/token-transfers)| +| BSC | `0xa4080f1778e69467e905b8d6f72f6e441f9e9484` [↗](https://bscscan.com/token/0xa4080f1778e69467e905b8d6f72f6e441f9e9484)| +| Canto | `0x555982d2E211745b96736665e19D9308B615F78e` [↗](https://canto.dex.guru/address/0x555982d2e211745b96736665e19d9308b615f78e)| +| Cronos | `0xFD0F80899983b8D46152aa1717D76cba71a31616` [↗](https://cronoscan.com/address/0xFD0F80899983b8D46152aa1717D76cba71a31616)| +| DFK | `0xB6b5C854a8f71939556d4f3a2e5829F7FcC1bf2A` [↗](https://subnets.avax.network/defi-kingdoms/dfk-chain/explorer/address/0xB6b5C854a8f71939556d4f3a2e5829F7FcC1bf2A)| +| Dogechain | `0xDfA53EeBA61D69E1D2b56b36d78449368F0265c1` [↗](https://explorer.dogechain.dog/address/0xDfA53EeBA61D69E1D2b56b36d78449368F0265c1)| +| Ethereum | `0x0f2D719407FdBeFF09D87557AbB7232601FD9F29` [↗](https://etherscan.io/token/0x0f2D719407FdBeFF09D87557AbB7232601FD9F29)| +| Fantom | `0xE55e19Fb4F2D85af758950957714292DAC1e25B2` [↗](https://ftmscan.com/address/0xe55e19fb4f2d85af758950957714292dac1e25b2)| +| Harmony | `0xE55e19Fb4F2D85af758950957714292DAC1e25B2` [↗](https://explorer.harmony.one/address/0xe55e19fb4f2d85af758950957714292dac1e25b2)| +| Moonbeam | `0xF44938b0125A6662f9536281aD2CD6c499F22004` [↗](https://moonscan.io/address/0xF44938b0125A6662f9536281aD2CD6c499F22004)| +| Moonriver | `0xd80d8688b02B3FD3afb81cDb124F188BB5aD0445` [↗](https://moonriver.moonscan.io/address/0xd80d8688b02B3FD3afb81cDb124F188BB5aD0445)| +| Optimism | `0x5A5fFf6F753d7C11A56A52FE47a177a87e431655` [↗](https://optimistic.etherscan.io/address/0x5A5fFf6F753d7C11A56A52FE47a177a87e431655)| +| Polygon | `0xf8f9efc0db77d8881500bb06ff5d6abc3070e695` [↗](https://polygonscan.com/token/0xf8f9efc0db77d8881500bb06ff5d6abc3070e695)| +| Metis | `0x67c10c397dd0ba417329543c1a40eb48aaa7cd00` [↗](https://andromeda-explorer.metis.io/address/0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00)| \ No newline at end of file diff --git a/docs/bridge/docs/05-Contracts/02-Synapse-Router.md b/docs/bridge/docs/05-Contracts/02-Synapse-Router.md new file mode 100644 index 0000000000..2b0a9bc21e --- /dev/null +++ b/docs/bridge/docs/05-Contracts/02-Synapse-Router.md @@ -0,0 +1,44 @@ +--- +title: Synapse Router +--- + +:::note This list may be incomplete + +The canonical list is hosted within the SynapseCNS on [Github](https://github.com/synapsecns/synapse-contracts). + +::: + +# Synapse Router + +Synapse Router contracts route through the [SynapseBridge](https://github.com/synapsecns/synapse-contracts/blob/ed93453430635e2d43704d5599d3318c43a23033/contracts/bridge/SynapseBridge.sol#L63-L118) contract, which is used for event indexing. + +:::tip Events + +Contracts in the `deployments` folder of each chain's `SynapseBridge.json` file emit `TokenMint` or `TokenWithdraw` events when a transaction completes on the destination chain. + +::: + +**Router address**: `0x7E7A0e201FD38d3ADAA9523Da6C109a07118C96a` + +| Chain | Address | +|-----------|----------------------------------------------| +| Arbitrum | `0x6F4e8eBa4D337f874Ab57478AcC2Cb5BACdc19c9` [↗](https://arbiscan.io/address/0x6F4e8eBa4D337f874Ab57478AcC2Cb5BACdc19c9) | +| Aurora | `0xaeD5b25BE1c3163c907a471082640450F928DDFE` [↗](https://explorer.mainnet.aurora.dev/address/0xaeD5b25BE1c3163c907a471082640450F928DDFE/transactions) | +| Avalanche | `0xC05e61d0E7a63D27546389B7aD62FdFf5A91aACE` [↗](https://snowtrace.io/address/0xC05e61d0E7a63D27546389B7aD62FdFf5A91aACE) | +| Base | `0xf07d1C752fAb503E47FEF309bf14fbDD3E867089` [↗](https://basescan.org/address/0xf07d1C752fAb503E47FEF309bf14fbDD3E867089) | +| Blast | `0x55769baf6ec39b3bf4aae948eb890ea33307ef3c` [↗](https://blastscan.io/address/0x55769baf6ec39b3bf4aae948eb890ea33307ef3c) | +| Boba | `0x432036208d2717394d2614d6697c46DF3Ed69540` [↗](https://blockexplorer.boba.network/address/0x432036208d2717394d2614d6697c46DF3Ed69540/transactions) | +| BSC | `0xd123f70AE324d34A9E76b67a27bf77593bA8749f` [↗](https://bscscan.com/address/0xd123f70AE324d34A9E76b67a27bf77593bA8749f) | +| Canto | `0xDde5BEC4815E1CeCf336fb973Ca578e8D83606E0` [↗](https://evm.explorer.canto.io/address/0xDde5BEC4815E1CeCf336fb973Ca578e8D83606E0) | +| Cronos | `0xE27BFf97CE92C3e1Ff7AA9f86781FDd6D48F5eE9` [↗](https://cronoscan.com/address/0xE27BFf97CE92C3e1Ff7AA9f86781FDd6D48F5eE9) | +| DFK | `0xE05c976d3f045D0E6E7A6f61083d98A15603cF6A` [↗](https://subnets.avax.network/defi-kingdoms/dfk-chain/explorer/address/0xE05c976d3f045D0E6E7A6f61083d98A15603cF6A) | +| Dogechain | `0x9508BF380c1e6f751D97604732eF1Bae6673f299` [↗](https://explorer.dogechain.dog/address/0x9508BF380c1e6f751D97604732eF1Bae6673f299) | +| Ethereum | `0x2796317b0fF8538F253012862c06787Adfb8cEb6` [↗](https://etherscan.io/address/0x2796317b0fF8538F253012862c06787Adfb8cEb6) | +| Fantom | `0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b` [↗](https://ftmscan.com/address/0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b) | +| Harmony | `0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b` [↗](https://explorer.harmony.one/address/0xaf41a65f786339e7911f4acdad6bd49426f2dc6b) | +| Klaytn | `0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b` [↗](https://scope.klaytn.com/account/0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b?tabId=txList) | +| Metis | `0x06Fea8513FF03a0d3f61324da709D4cf06F42A5c` [↗](https://andromeda-explorer.metis.io/address/0x06Fea8513FF03a0d3f61324da709D4cf06F42A5c) | +| Moonbeam | `0x84A420459cd31C3c34583F67E0f0fB191067D32f` [↗](https://moonscan.io/address/0x84A420459cd31C3c34583F67E0f0fB191067D32f) | +| Moonriver | `0xaeD5b25BE1c3163c907a471082640450F928DDFE` [↗](https://moonriver.moonscan.io/address/0xaeD5b25BE1c3163c907a471082640450F928DDFE) | +| Optimism | `0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b` [↗](https://optimistic.etherscan.io/address/0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b) | +| Polygon | `0x8F5BBB2BB8c2Ee94639E55d5F41de9b4839C1280` [↗](https://polygonscan.com/address/0x8F5BBB2BB8c2Ee94639E55d5F41de9b4839C1280) | diff --git a/docs/bridge/docs/05-Contracts/03-Intermediate-Tokens.md b/docs/bridge/docs/05-Contracts/03-Intermediate-Tokens.md new file mode 100644 index 0000000000..5b493cf9ca --- /dev/null +++ b/docs/bridge/docs/05-Contracts/03-Intermediate-Tokens.md @@ -0,0 +1,41 @@ +--- +title: Intermediate Tokens +--- + +:::note This list may be incomplete + +The canonical list is hosted within the SynapseCNS on [Github](https://github.com/synapsecns/synapse-contracts). + +::: + +# Intermediate Tokens + +## nETH + +| Chain | Address | +|-----------|----------------------------------------------| +| Arbitrum | `0x3ea9B0ab55F34Fb188824Ee288CeaEfC63cf908e` [↗](https://arbiscan.io/address/0x3ea9B0ab55F34Fb188824Ee288CeaEfC63cf908e) | +| Base | `0xb554A55358fF0382Fb21F0a478C3546d1106Be8c` [↗](https://basescan.org/address/0xb554A55358fF0382Fb21F0a478C3546d1106Be8c) | +| Blast | `0xce971282faac9fabcf121944956da7142cccc855` [↗](https://blastscan.io/token/0xce971282faac9fabcf121944956da7142cccc855) | +| Boba | `0x96419929d7949D6A801A6909c145C8EEf6A40431` [↗](https://blockexplorer.boba.network/address/0x96419929d7949D6A801A6909c145C8EEf6A40431/transactions) | +| Optimism | `0x809DC529f07651bD43A172e8dB6f4a7a0d771036` [↗](https://optimistic.etherscan.io/address/0x809DC529f07651bD43A172e8dB6f4a7a0d771036) | +| Metis | `0x931b8f17764362a3325d30681009f0edd6211231` [↗](https://andromeda-explorer.metis.io/address/0x931B8f17764362A3325D30681009f0eDd6211231) | + +## nUSD + +| Chain | Address | +|-----------|----------------------------------------------| +| Arbitrum | `0x2913e812cf0dcca30fb28e6cac3d2dcff4497688` [↗](https://arbiscan.io/token/0x2913e812cf0dcca30fb28e6cac3d2dcff4497688) | +| Aurora | `0x07379565cD8B0CaE7c60Dc78e7f601b34AF2A21c` [↗](https://explorer.mainnet.aurora.dev/address/0x07379565cD8B0CaE7c60Dc78e7f601b34AF2A21c/transactions) | +| Avalanche | `0xCFc37A6AB183dd4aED08C204D1c2773c0b1BDf46` [↗](https://snowtrace.io/address/0xCFc37A6AB183dd4aED08C204D1c2773c0b1BDf46) | +| Blast | `0x3194B0A295D87fDAA54DF852c248F7a6BAF6c6e0` [↗](https://blastscan.io/address/0x3194B0A295D87fDAA54DF852c248F7a6BAF6c6e0) | +| Boba | `0x6B4712AE9797C199edd44F897cA09BC57628a1CF` [↗](https://blockexplorer.boba.network/tokens/0x6B4712AE9797C199edd44F897cA09BC57628a1CF/token-transfers) | +| BSC | `0x23b891e5c62e0955ae2bd185990103928ab817b3` [↗](https://bscscan.com/token/0x23b891e5c62e0955ae2bd185990103928ab817b3) | +| Cronos | `0x396c9c192dd323995346632581BEF92a31AC623b` [↗](https://cronoscan.com/address/0x396c9c192dd323995346632581BEF92a31AC623b) | +| DFK | `0x52285D426120aB91F378b3dF4A15a036a62200aE` [↗](https://subnets.avax.network/defi-kingdoms/dfk-chain/explorer/address/0x52285D426120aB91F378b3dF4A15a036a62200aE) | +| Ethereum | `0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F` [↗](https://etherscan.io/token/0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F) | +| Fantom | `0xed2a7edd7413021d440b09d654f3b87712abab66` [↗](https://ftmscan.com/token/0xed2a7edd7413021d440b09d654f3b87712abab66) | +| Harmony | `0xed2a7edd7413021d440b09d654f3b87712abab66` [↗](https://explorer.harmony.one/address/0xed2a7edd7413021d440b09d654f3b87712abab66) | +| Optimism | `0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00` [↗](https://optimistic.etherscan.io/address/0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00) | +| Polygon | `0xb6c473756050de474286bed418b77aeac39b02af` [↗](https://polygonscan.com/token/0xb6c473756050de474286bed418b77aeac39b02af) | +| Metis | `0x961318fc85475e125b99cc9215f62679ae5200ab` [↗](https://andromeda-explorer.metis.io/address/0x961318Fc85475E125B99Cc9215f62679aE5200aB) | diff --git a/docs/bridge/docs/05-Contracts/04-Liquidity-Pools.md b/docs/bridge/docs/05-Contracts/04-Liquidity-Pools.md new file mode 100644 index 0000000000..5f3b5a887e --- /dev/null +++ b/docs/bridge/docs/05-Contracts/04-Liquidity-Pools.md @@ -0,0 +1,41 @@ +--- +title: Liquidity Pools +--- + +:::note This list may be incomplete + +The canonical list is hosted within the SynapseCNS on [Github](https://github.com/synapsecns/synapse-contracts). + +::: + +# Liquidity Pools + +Liquidity pools are available at [https://synapseprotocol.com/pools](https://synapseprotocol.com/pools) + +## ETH Pools + +| Chain | Address | +|-----------|----------------------------------------------| +| Arbitrum | `0xa067668661C84476aFcDc6fA5D758C4c01C34352` [↗](https://arbiscan.io/address/0x6f4e8eba4d337f874ab57478acc2cb5bacdc19c9) | +| Avalanche | `0x77a7e60555bC18B4Be44C181b2575eee46212d44` [↗](https://snowtrace.io/address/0x77a7e60555bC18B4Be44C181b2575eee46212d44) | +| Base | `0x6223bD82010E2fB69F329933De20897e7a4C225f` [↗](https://basescan.org/address/0x6223bd82010e2fb69f329933de20897e7a4c225f) | +| Blast | `0x999fcd13C54B26E02a6Ccd185f71550b3a4641c0` [↗](https://blastscan.io/address/0x999fcd13C54B26E02a6Ccd185f71550b3a4641c0) | +| Metis | `0x09fEC30669d63A13c666d2129230dD5588E2e240` [↗](https://andromeda-explorer.metis.io/address/0x09fEC30669d63A13c666d2129230dD5588E2e240) | +| Optimism | `0xE27BFf97CE92C3e1Ff7AA9f86781FDd6D48F5eE9` [↗](https://optimistic.etherscan.io/address/0xE27BFf97CE92C3e1Ff7AA9f86781FDd6D48F5eE9) | + +## Stableswap Pools + +| Chain | Address | +|-----------|----------------------------------------------| +| Arbitrum | `0x9Dd329F5411466d9e0C488fF72519CA9fEf0cb40` [↗](https://arbiscan.io/address/0x9Dd329F5411466d9e0C488fF72519CA9fEf0cb40) | +| Aurora | `0x3CE7AAD78B9eb47Fd2b487c463A17AAeD038B7EC` [↗](https://explorer.aurora.dev/address/0x3CE7AAD78B9eb47Fd2b487c463A17AAeD038B7EC) | +| Avalanche | `0xED2a7edd7413021d440b09D654f3b87712abAB66` [↗](https://snowtrace.io/address/0xED2a7edd7413021d440b09D654f3b87712abAB66) | +| Blast | `0xa4bd1AAD7cF04567c10f38FC4355E91bba32aC9c` [↗](https://blastscan.io/address/0xa4bd1AAD7cF04567c10f38FC4355E91bba32aC9c) | +| BNB Chain | `0x28ec0B36F0819ecB5005cAB836F4ED5a2eCa4D13` [↗](https://bscscan.com/address/0x28ec0B36F0819ecB5005cAB836F4ED5a2eCa4D13) | +| Boba | `0x75FF037256b36F15919369AC58695550bE72fead` [↗](https://bobascan.com/address/0x75FF037256b36F15919369AC58695550bE72fead) | +| Canto | `0x07379565cD8B0CaE7c60Dc78e7f601b34AF2A21c` [↗](https://evm.explorer.canto.io/address/0x07379565cD8B0CaE7c60Dc78e7f601b34AF2A21c) | +| Ethereum | `0x1116898DdA4015eD8dDefb84b6e8Bc24528Af2d8` [↗](https://etherscan.io/address/0x1116898DdA4015eD8dDefb84b6e8Bc24528Af2d8) | +| Fantom | `0x85662fd123280827e11C59973Ac9fcBE838dC3B4` [↗](https://ftmscan.com/address/0x85662fd123280827e11C59973Ac9fcBE838dC3B4) | +| Metis | `0x555982d2E211745b96736665e19D9308B615F78e` [↗](https://andromeda-explorer.metis.io/address/0x555982d2E211745b96736665e19D9308B615F78e) | +| Optimism | `0xF44938b0125A6662f9536281aD2CD6c499F22004` [↗](https://optimistic.etherscan.io/address/0xf44938b0125a6662f9536281ad2cd6c499f22004) | +| Polygon | `0x85fCD7Dd0a1e1A9FCD5FD886ED522dE8221C3EE5` [↗](https://polygonscan.com/address/0x85fCD7Dd0a1e1A9FCD5FD886ED522dE8221C3EE5) | diff --git a/docs/bridge/docs/05-Contracts/05-CCTP.md b/docs/bridge/docs/05-Contracts/05-CCTP.md new file mode 100644 index 0000000000..44a96c48c9 --- /dev/null +++ b/docs/bridge/docs/05-Contracts/05-CCTP.md @@ -0,0 +1,21 @@ +:::note This list may be incomplete + +The canonical list is hosted within the SynapseCNS on [Github](https://github.com/synapsecns/synapse-contracts). + +::: + +# CCTP + +Synapse CCTP contracts route through the [SynapseBridge](https://github.com/synapsecns/synapse-contracts/blob/ed93453430635e2d43704d5599d3318c43a23033/contracts/bridge/SynapseBridge.sol#L63-L118) contract to interact with [Circle CCTP contracts](https://developers.circle.com/stablecoins/docs/evm-smart-contracts) which mint and burn USDC across supported chains. + +**Address**: `0xd5a597d6e7ddf373a92c8f477daaa673b0902f48`\ +**Contract**: [SynapseCCTP.sol](https://github.com/synapsecns/synapse-contracts/blob/master/contracts/cctp/SynapseCCTP.sol) + +| Chain | Address | +| --------- | ---------------------------------------------| +| Arbitrum | `0x12715a66773bd9c54534a01abf01d05f6b4bd35e` [↗](https://arbiscan.io/address/0x12715a66773bd9c54534a01abf01d05f6b4bd35e) | +| Avalanche | `0x12715a66773bd9c54534a01abf01d05f6b4bd35e` [↗](https://snowtrace.io/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | +| Base | `0x12715a66773bd9c54534a01abf01d05f6b4bd35e` [↗](https://basescan.org/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | +| Ethereum | `0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E` [↗](https://etherscan.io/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | +| Optimism | `0x12715a66773bd9c54534a01abf01d05f6b4bd35e` [↗](https://optimistic.etherscan.io/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | +| Polygon | `0x12715a66773bd9c54534a01abf01d05f6b4bd35e` [↗](https://polygonscan.com/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | diff --git a/docs/bridge/docs/05-Contracts/06-RFQ.md b/docs/bridge/docs/05-Contracts/06-RFQ.md new file mode 100644 index 0000000000..8f76cb9468 --- /dev/null +++ b/docs/bridge/docs/05-Contracts/06-RFQ.md @@ -0,0 +1,28 @@ +--- +title: RFQ +--- + +:::note This list may be incomplete + +The canonical list is hosted within the SynapseCNS on [Github](https://github.com/synapsecns/synapse-contracts). + +::: + +# RFQ + +RFQ contracts route through the [SynapseBridge](https://github.com/synapsecns/synapse-contracts/blob/ed93453430635e2d43704d5599d3318c43a23033/contracts/bridge/SynapseBridge.sol#L63-L118) contract. + +**Source code**: [SynapseCNS (Github)](https://github.com/synapsecns/sanguine/tree/master/packages/contracts-rfq)\ +**Generated docs**: [RFQ docs](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html)\ +**Address**: `0x00cD000000003f7F682BE4813200893d4e690000` + +| Chain | Address | +| -------- | ------- | +| Arbitrum | `0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E` [↗](https://arbiscan.io/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | +| Base | `0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E` [↗](https://basescan.org/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | +| Ethereum | `0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E` [↗](https://etherscan.io/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | +| Optimism | `0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E` [↗](https://optimistic.etherscan.io/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | +| Scroll | `0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E` [↗](https://scrollscan.com/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | +| Linea | `0x34F52752975222d5994C206cE08C1d5B329f24dD` [↗](https://lineascan.build/address/0x34F52752975222d5994C206cE08C1d5B329f24dD) | +| BNB Chain| `0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E` [↗](https://bscscan.com/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | +| Blast | `0x34F52752975222d5994C206cE08C1d5B329f24dD` [↗](https://blastscan.io/address/0x34F52752975222d5994C206cE08C1d5B329f24dD) | diff --git a/docs/bridge/docs/05-Contracts/07-Bridge-Zaps.md b/docs/bridge/docs/05-Contracts/07-Bridge-Zaps.md new file mode 100644 index 0000000000..e5e0bcc6d6 --- /dev/null +++ b/docs/bridge/docs/05-Contracts/07-Bridge-Zaps.md @@ -0,0 +1,30 @@ +--- +title: Bridge Zaps +--- + +:::note This list may be incomplete + +The canonical list is hosted within the SynapseCNS on [Github](https://github.com/synapsecns/synapse-contracts). + +::: + +# Bridge Zaps + +| Chain | Address | +|-----------|----------------------------------------------| +| Arbitrum | `0x37f9aE2e0Ea6742b9CAD5AbCfB6bBC3475b3862B` [↗](https://arbiscan.io/address/0x37f9aE2e0Ea6742b9CAD5AbCfB6bBC3475b3862B) | +| Aurora | `0x2D8Ee8d6951cB4Eecfe4a79eb9C2F973C02596Ed` [↗](https://aurorascan.dev/address/0x2D8Ee8d6951cB4Eecfe4a79eb9C2F973C02596Ed) | +| Avalanche | `0x0EF812f4c68DC84c22A4821EF30ba2ffAB9C2f3A` [↗](https://snowtrace.io/address/0x0EF812f4c68DC84c22A4821EF30ba2ffAB9C2f3A) | +| Boba | `0x64B4097bCCD27D49BC2A081984C39C3EeC427a2d` [↗](https://blockexplorer.boba.network/address/0x64B4097bCCD27D49BC2A081984C39C3EeC427a2d/transactions) | +| BSC | `0x749F37Df06A99D6A8E065dd065f8cF947ca23697` [↗](https://bscscan.com/address/0x749F37Df06A99D6A8E065dd065f8cF947ca23697) | +| Canto | `0x8671A0465844a15eb7230C5dd8d6032c26c655B7` [↗](https://evm.explorer.canto.io/address/0x8671A0465844a15eb7230C5dd8d6032c26c655B7) | +| Cronos | `0x991adb00eF4c4a6D1eA6036811138Db4379377C2` [↗](https://cronoscan.com/address/0x991adb00eF4c4a6D1eA6036811138Db4379377C2) | +| DFK | `0x75224b0f245Fe51d5bf47A898DbB6720D4150BA7` [↗](https://subnets.avax.network/defi-kingdoms/dfk-chain/explorer/address/0x75224b0f245Fe51d5bf47A898DbB6720D4150BA7) | +| Dogechain | `0x544450Ffdfa5EA20528F21918E8aAC7B2C733381` [↗](https://explorer.dogechain.dog/address/0x544450Ffdfa5EA20528F21918E8aAC7B2C733381) | +| Ethereum | `0x6571d6be3d8460CF5F7d6711Cd9961860029D85F` [↗](https://etherscan.io/address/0x6571d6be3d8460CF5F7d6711Cd9961860029D85F) | +| Fantom | `0xB003e75f7E0B5365e814302192E99b4EE08c0DEd` [↗](https://ftmscan.com/address/0xB003e75f7E0B5365e814302192E99b4EE08c0DEd) | +| Harmony | `0xB003e75f7E0B5365e814302192E99b4EE08c0DEd` [↗](https://explorer.harmony.one/address/0xb003e75f7e0b5365e814302192e99b4ee08c0ded) | +| Optimism | `0x470f9522ff620eE45DF86C58E54E6A645fE3b4A7` [↗](https://optimistic.etherscan.io/address/0x470f9522ff620eE45DF86C58E54E6A645fE3b4A7) | +| Moonbeam | `0x73783F028c60D463bc604cc53852C37C31dEC5e9` [↗](https://moonscan.io/address/0x73783F028c60D463bc604cc53852C37C31dEC5e9) | +| Moonriver | `0x06Fea8513FF03a0d3f61324da709D4cf06F42A5c` [↗](https://moonriver.moonscan.io/address/0x06Fea8513FF03a0d3f61324da709D4cf06F42A5c) | +| Polygon | `0x1c6aE197fF4BF7BA96c66C5FD64Cb22450aF9cC8` [↗](https://polygonscan.com/address/0x1c6aE197fF4BF7BA96c66C5FD64Cb22450aF9cC8) | diff --git a/docs/bridge/docs/05-Contracts/08-MiniChef.md b/docs/bridge/docs/05-Contracts/08-MiniChef.md new file mode 100644 index 0000000000..9484a68f79 --- /dev/null +++ b/docs/bridge/docs/05-Contracts/08-MiniChef.md @@ -0,0 +1,29 @@ +--- +title: MiniChef +--- + +:::note This list may be incomplete + +The canonical list is hosted within the SynapseCNS on [Github](https://github.com/synapsecns/synapse-contracts). + +::: + +# MiniChef + +| Chain | Address | +|-----------|----------------------------------------------| +| Arbitrum | `0x73186f2Cf2493f20836b17b21ae79fc12934E207` [↗](https://arbiscan.io/address/0x73186f2Cf2493f20836b17b21ae79fc12934E207) | +| Aurora | `0x809DC529f07651bD43A172e8dB6f4a7a0d771036` [↗](https://explorer.mainnet.aurora.dev/address/0x809DC529f07651bD43A172e8dB6f4a7a0d771036/transactions) | +| Avalanche | `0x3a01521F8E7F012eB37eAAf1cb9490a5d9e18249` [↗](https://snowtrace.io/address/0x3a01521F8E7F012eB37eAAf1cb9490a5d9e18249) | +| Base | `0xfFC2d603fde1F99ad94026c00B6204Bb9b8c36E9` [↗](https://basescan.org/address/0xfFC2d603fde1F99ad94026c00B6204Bb9b8c36E9) | +| Blast | `0x3100dC8464A8523306c3C5034de24a8927d6E590` [↗](https://blastscan.io/address/0x3100dC8464A8523306c3C5034de24a8927d6E590) | +| Boba | `0xd5609cD0e1675331E4Fb1d43207C8d9D83AAb17C` [↗](https://blockexplorer.boba.network/address/0xd5609cD0e1675331E4Fb1d43207C8d9D83AAb17C/transactions) | +| BSC | `0x8F5BBB2BB8c2Ee94639E55d5F41de9b4839C1280` [↗](https://bscscan.com/address/0x8F5BBB2BB8c2Ee94639E55d5F41de9b4839C1280) | +| Canto | `0x93124c923dA389Bc0f13840fB822Ce715ca67ED6` [↗](https://canto.dex.guru/address/0x93124c923dA389Bc0f13840fB822Ce715ca67ED6) | +| Ethereum | `0xd10eF2A513cEE0Db54E959eF16cAc711470B62cF` [↗](https://etherscan.io/address/0xd10eF2A513cEE0Db54E959eF16cAc711470B62cF) | +| Fantom | `0xaeD5b25BE1c3163c907a471082640450F928DDFE` [↗](https://ftmscan.com/address/0xaed5b25be1c3163c907a471082640450f928ddfe) | +| Harmony | `0xaeD5b25BE1c3163c907a471082640450F928DDFE` [↗](https://explorer.harmony.one/address/0xaed5b25be1c3163c907a471082640450f928ddfe) | +| Metis | `0xaB0D8Fc46249DaAcd5cB36c5F0bC4f0DAF34EBf5` [↗](https://andromeda-explorer.metis.io/address/0xaB0D8Fc46249DaAcd5cB36c5F0bC4f0DAF34EBf5) | +| Moonriver | `0x432036208d2717394d2614d6697c46DF3Ed69540` [↗](https://moonriver.moonscan.io/address/0x432036208d2717394d2614d6697c46DF3Ed69540) | +| Optimism | `0xe8c610fcb63A4974F02Da52f0B4523937012Aaa0` [↗](https://optimistic.etherscan.io/address/0xe8c610fcb63A4974F02Da52f0B4523937012Aaa0) | +| Polygon | `0x7875Af1a6878bdA1C129a4e2356A3fD040418Be5` [↗](https://polygonscan.com/address/0x7875Af1a6878bdA1C129a4e2356A3fD040418Be5) | diff --git a/docs/bridge/docs/Services/Scribe.md b/docs/bridge/docs/06-Services/01-Scribe.md similarity index 95% rename from docs/bridge/docs/Services/Scribe.md rename to docs/bridge/docs/06-Services/01-Scribe.md index 0b58a1edab..6af5fd2de8 100644 --- a/docs/bridge/docs/Services/Scribe.md +++ b/docs/bridge/docs/06-Services/01-Scribe.md @@ -1,8 +1,3 @@ ---- -sidebar_position: 0 -sidebar_label: Scribe ---- - # Scribe Scribe is a multi-chain indexing service designed to store logs, receipts, and transactions for every event from specified contracts across multiple blockchains. It provides a powerful tool for analytics, event streaming, and monitoring on-chain activities. @@ -89,7 +84,7 @@ chains: Key configuration parameters include: -- `rpc_url`: The omnirpc url to use for querying chain data (no trailing slash). For more information on omnirpc, see [here](Services//Omnirpc.md). +- `rpc_url`: The omnirpc url to use for querying chain data (no trailing slash). For more information on omnirpc, see [here](Omnirpc). - `chains`: List of chains to index, including chain-specific parameters: - `chain_id`: The ID of the chain. - `get_logs_range`: The number of blocks to request in a single `getLogs` request. @@ -117,4 +112,4 @@ For a full list of available queries, refer to the GraphQL schema. ## Observability -Scribe implements open telemetry for both tracing and metrics. Please see the [Observability](../Observability) page for more info. +Scribe implements open telemetry for both tracing and metrics. Please see the [Observability](Observability) page for more info. diff --git a/docs/bridge/docs/Services/Omnirpc.md b/docs/bridge/docs/06-Services/02-Omnirpc.md similarity index 89% rename from docs/bridge/docs/Services/Omnirpc.md rename to docs/bridge/docs/06-Services/02-Omnirpc.md index e719fc0278..f0b804f104 100644 --- a/docs/bridge/docs/Services/Omnirpc.md +++ b/docs/bridge/docs/06-Services/02-Omnirpc.md @@ -1,10 +1,12 @@ -Omnirpc is an rpc load balancer and verifier that allows users to query chain data from multiple chains. It is a service that should be run by Quoters and interfaces that allow Solvers to post quotes. Omnirpc takes in a yaml config that allows the user to specify which chains it should run on. +# OmniRPC -### Running OmniRPC +OmniRPC is an RPC load balancer and verifier that allows users to query chain data from multiple chains. It is a service that should be run by Quoters and interfaces that allow Solvers to post quotes. OmniRPC takes in a yaml config that allows the user to specify which chains it should run on. + +## Running OmniRPC ### Building From Source -To build omnirpc from source, you will need to have Go installed. You can install Go by following the instructions [here](https://golang.org/doc/install). Once you have Go installed, you can build the relayer by running the following commands: +To build OmniRPC from source, you will need to have Go installed. You can install Go by following the instructions [here](https://golang.org/doc/install). Once you have Go installed, you can build the relayer by running the following commands: 1. `git clone https://github.com/synapsecns/sanguine --recursive` 2. `cd sanguine/services/omnirpc` @@ -18,11 +20,11 @@ The relayer can also be run with docker. To do this, you will need to pull the [ docker run ghcr.io/synapsecns/sanguine/omnirpc:latest --config /path/to/config ``` -There is also a helm chart available for omnirpc [here](https://artifacthub.io/packages/helm/synapse/omnirpc). +There is also a helm chart available for OmniRPC [here](https://artifacthub.io/packages/helm/synapse/omnirpc). ### Configuration -Omnirpc is configured with a yaml file. The following is an example configuration: +OmniRPC is configured with a yaml file. The following is an example configuration: ```yaml chains: diff --git a/docs/bridge/docs/Services/Signer.md b/docs/bridge/docs/06-Services/03-Signer.md similarity index 100% rename from docs/bridge/docs/Services/Signer.md rename to docs/bridge/docs/06-Services/03-Signer.md diff --git a/docs/bridge/docs/Services/Submitter.md b/docs/bridge/docs/06-Services/04-Submitter.md similarity index 98% rename from docs/bridge/docs/Services/Submitter.md rename to docs/bridge/docs/06-Services/04-Submitter.md index 379145a999..afa6435d42 100644 --- a/docs/bridge/docs/Services/Submitter.md +++ b/docs/bridge/docs/06-Services/04-Submitter.md @@ -1,6 +1,8 @@ -# Submitter +:::note In-progress -This section is still in progress, please see [here](https://pkg.go.dev/github.com/synapsecns/sanguine/ethergo/submitter#section-readme) for details. +Please see the [Go Submitter documentation page](https://pkg.go.dev/github.com/synapsecns/sanguine/ethergo/submitter#section-readme) for more details. + +::: # Ethergo Submitter diff --git a/docs/bridge/docs/Observability.md b/docs/bridge/docs/06-Services/05-Observability.md similarity index 99% rename from docs/bridge/docs/Observability.md rename to docs/bridge/docs/06-Services/05-Observability.md index 3a9fb2fadd..69466e087f 100644 --- a/docs/bridge/docs/Observability.md +++ b/docs/bridge/docs/06-Services/05-Observability.md @@ -1,3 +1,5 @@ +# Observability + All off-chain systems are by default observable and configured through the [metrics](https://pkg.go.dev/github.com/synapsecns/sanguine/core/metrics#section-readme) package. The observability stack is built around [open telemetry](https://opentelemetry.io/) with metrics also being exported using this standard. "Metrics" themselves are divided into 3 different types of metrics: diff --git a/docs/bridge/docs/Services/img/signer/aws/create-acces-key.png b/docs/bridge/docs/06-Services/img/signer/aws/create-acces-key.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/create-acces-key.png rename to docs/bridge/docs/06-Services/img/signer/aws/create-acces-key.png diff --git a/docs/bridge/docs/Services/img/signer/aws/create-access-key.png b/docs/bridge/docs/06-Services/img/signer/aws/create-access-key.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/create-access-key.png rename to docs/bridge/docs/06-Services/img/signer/aws/create-access-key.png diff --git a/docs/bridge/docs/Services/img/signer/aws/iam-dash.png b/docs/bridge/docs/06-Services/img/signer/aws/iam-dash.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/iam-dash.png rename to docs/bridge/docs/06-Services/img/signer/aws/iam-dash.png diff --git a/docs/bridge/docs/Services/img/signer/aws/iam-preview-user.png b/docs/bridge/docs/06-Services/img/signer/aws/iam-preview-user.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/iam-preview-user.png rename to docs/bridge/docs/06-Services/img/signer/aws/iam-preview-user.png diff --git a/docs/bridge/docs/Services/img/signer/aws/key-details.png b/docs/bridge/docs/06-Services/img/signer/aws/key-details.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/key-details.png rename to docs/bridge/docs/06-Services/img/signer/aws/key-details.png diff --git a/docs/bridge/docs/Services/img/signer/aws/kms-1.png b/docs/bridge/docs/06-Services/img/signer/aws/kms-1.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/kms-1.png rename to docs/bridge/docs/06-Services/img/signer/aws/kms-1.png diff --git a/docs/bridge/docs/Services/img/signer/aws/kms-2.png b/docs/bridge/docs/06-Services/img/signer/aws/kms-2.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/kms-2.png rename to docs/bridge/docs/06-Services/img/signer/aws/kms-2.png diff --git a/docs/bridge/docs/Services/img/signer/aws/kms-advanced.png b/docs/bridge/docs/06-Services/img/signer/aws/kms-advanced.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/kms-advanced.png rename to docs/bridge/docs/06-Services/img/signer/aws/kms-advanced.png diff --git a/docs/bridge/docs/Services/img/signer/aws/kms-labels.png b/docs/bridge/docs/06-Services/img/signer/aws/kms-labels.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/kms-labels.png rename to docs/bridge/docs/06-Services/img/signer/aws/kms-labels.png diff --git a/docs/bridge/docs/Services/img/signer/aws/kms-permissons.png b/docs/bridge/docs/06-Services/img/signer/aws/kms-permissons.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/kms-permissons.png rename to docs/bridge/docs/06-Services/img/signer/aws/kms-permissons.png diff --git a/docs/bridge/docs/Services/img/signer/aws/kms-user-permissions.png b/docs/bridge/docs/06-Services/img/signer/aws/kms-user-permissions.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/kms-user-permissions.png rename to docs/bridge/docs/06-Services/img/signer/aws/kms-user-permissions.png diff --git a/docs/bridge/docs/Services/img/signer/aws/kms-user.png b/docs/bridge/docs/06-Services/img/signer/aws/kms-user.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/kms-user.png rename to docs/bridge/docs/06-Services/img/signer/aws/kms-user.png diff --git a/docs/bridge/docs/Services/img/signer/aws/perms.png b/docs/bridge/docs/06-Services/img/signer/aws/perms.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/perms.png rename to docs/bridge/docs/06-Services/img/signer/aws/perms.png diff --git a/docs/bridge/docs/Services/img/signer/aws/review.png b/docs/bridge/docs/06-Services/img/signer/aws/review.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/review.png rename to docs/bridge/docs/06-Services/img/signer/aws/review.png diff --git a/docs/bridge/docs/Services/img/signer/aws/user-list.png b/docs/bridge/docs/06-Services/img/signer/aws/user-list.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/user-list.png rename to docs/bridge/docs/06-Services/img/signer/aws/user-list.png diff --git a/docs/bridge/docs/Services/img/signer/aws/user-perms.png b/docs/bridge/docs/06-Services/img/signer/aws/user-perms.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/user-perms.png rename to docs/bridge/docs/06-Services/img/signer/aws/user-perms.png diff --git a/docs/bridge/docs/Services/img/submitter/metrics.png b/docs/bridge/docs/06-Services/img/submitter/metrics.png similarity index 100% rename from docs/bridge/docs/Services/img/submitter/metrics.png rename to docs/bridge/docs/06-Services/img/submitter/metrics.png diff --git a/docs/bridge/docs/07-Support/Transaction-Support.md b/docs/bridge/docs/07-Support/Transaction-Support.md new file mode 100644 index 0000000000..5ebf4ccbb9 --- /dev/null +++ b/docs/bridge/docs/07-Support/Transaction-Support.md @@ -0,0 +1,51 @@ +# Transaction Support FAQ + +## What does a Bridge transaction look like? + +After submitting and signing a transaction from your wallet, gas fees are collected, and the transaction is sent to the origin chain router. Once accepted, the bridged asset is removed from your portfolio, and a progress bar shows the estimated confirmation time. + +Once confirmed on the destination chain, the asset is added to your portfolio, and destination hash is available from the progress menu dropdown. The transaction appears as part of your history in the Activity tab once it is index by the Synapse Explorer. + +Gas token airdrops and rebates are delivered to your wallet automatically. However, only bridgeable assets are shown in your Synapse portfolio. + +## Did my transaction initiate? + +Transactions that do not initiate on the origin chain return an error message. Your assets will remain in your portfolio, under your control. + +In the event that your transaction does not initiate, double check that you have sufficient funds to send, and to cover gas fees, and you can safely try again. + +## My transaction failed to initiate after several tries + +Occasionally, technical issues or a high volume of activity on the origin chain may prevent new transactions from being accepted. + +In most cases, these issues safely resolve within 30-60 minutes. Activity levels can be found on native block explorers (e.g the [Etherscan gas tracker](https://etherscan.io/gastracker)). + +You can also adjust your wallet’s gas settings to make transactions more likely to be accepted during times of peak activity. + +## Why is my transaction taking so long? +Synapse time estimates are based on destination block times. Occasionally, a transaction may post to a later block than expected. + +Block explorer links in the progress dropdown menu can confirm whether a confirmation on-chain but not yet received by Synapse. + +## My transaction failed to complete + +Transactions that fail to complete are not lost, and are manually addressed by the Synapse support team. You can reach Support via the [Synapse Discord channel](https://discord.com/invite/synapseprotocol) at any time. + +:::note For DeFi Kingdoms + +NFT transactions can take twice as long as tokens. Contact Support if your transaction has been pending for two hours or more. + +::: + +## I received a different asset than expected +In the event of an sudden increase in slippage, Synapse will deliver the intermediate asset sent to the destination chain instead of swapping it for an unexpected loss. + +This asset appears in your portfolio and can be safely [swapped](https://synapseprotocol.com/swap) for the asset of your choice on the destination chain. + +## Did I receive my rebate or gas airdrop? +While rebates and airdrops appear in your wallet automatically, only bridgeable assets are shown in your Synapse portfolio. + +If you don’t see an asset you should have received, first check your wallet while connected to the destination chain for your bridge transaction. + +## Help! +Don’t panic! Contact Synapse Support on Discord to answer any other questions you might have. diff --git a/docs/bridge/docs/07-Support/index.md b/docs/bridge/docs/07-Support/index.md new file mode 100644 index 0000000000..12ad2e4940 --- /dev/null +++ b/docs/bridge/docs/07-Support/index.md @@ -0,0 +1,12 @@ +# Support + +Connect with other developers and the Synapse team + +* **[Discord](https://discord.gg/synapseprotocol)** +* **[Twitter](https://twitter.com/SynapseProtocol)** +* **[Telegram](https://t.me/synapseprotocol)** +* **[Forum](https://forum.synapseprotocol.com/)** + +## Frequently Asked Questions + +* [Transaction Support FAQ](Transaction-Support) diff --git a/docs/bridge/docs/CCTP/Contracts.md b/docs/bridge/docs/CCTP/Contracts.md deleted file mode 100644 index 5707af69a6..0000000000 --- a/docs/bridge/docs/CCTP/Contracts.md +++ /dev/null @@ -1,18 +0,0 @@ -# Contracts - -Synapse CCTP contracts deployed on several chains and are documented inline. Synapse CCTP routes tokens through the CCTP module and [SynapseCCTP](https://github.com/synapsecns/synapse-contracts/blob/master/contracts/cctp/SynapseCCTP.sol) interacts with the Circle contracts to mint/burn USDC. These contracts sit on top of the Circle CCTP contracts and are responsible for minting and burning USDC on supported chains. - -### Synapse CCTP - -| Chain | Address | -| --------- | -------------------------------------------------------------------------------------------------------------------------------- | -| Arbitrum | [0x12715a66773bd9c54534a01abf01d05f6b4bd35e](https://arbiscan.io/address/0x12715a66773bd9c54534a01abf01d05f6b4bd35e) | -| Avalanche | [0x12715a66773bd9c54534a01abf01d05f6b4bd35e](https://snowtrace.io/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | -| Base | [0x12715a66773bd9c54534a01abf01d05f6b4bd35e](https://basescan.org/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | -| Ethereum | [0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E](https://etherscan.io/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | -| Optimism | [0x12715a66773bd9c54534a01abf01d05f6b4bd35e](https://optimistic.etherscan.io/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | -| Polygon | [0x12715a66773bd9c54534a01abf01d05f6b4bd35e](https://polygonscan.com/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | - -## Circle Contracts - -Please refer to [this page](https://developers.circle.com/stablecoins/docs/evm-smart-contracts) for Circle contract addresses. diff --git a/docs/bridge/docs/CCTP/Overview.md b/docs/bridge/docs/CCTP/Overview.md deleted file mode 100644 index cc6f4b1fc8..0000000000 --- a/docs/bridge/docs/CCTP/Overview.md +++ /dev/null @@ -1,8 +0,0 @@ -Synapse CCTP is a custom module built on top of Circle's [Cross-Chain Transfer Protocol](https://www.circle.com/en/cross-chain-transfer-protocol) that allows for bridge requests to natively mint & burn USDC on [supported chains](https://developers.circle.com/stablecoins/docs/cctp-getting-started#supported-blockchains). - -Synapse's CCTP implementation consists of two main components: - -- [CCTP Relayer](./Relayer.md): An off-chain service that fulfills transactions requested through the CCTP contracts. The relayer is responsible for fetching attestations from the [Circle API](https://developers.circle.com/stablecoin/reference) and submitting them to the CCTP contracts. Anyone can run a relayer. -- [CCTP Contracts](./Contracts.md): A set of smart contracts that allow for the minting and burning of USDC on supported chains, and instant swaps to/from any supported asset. These contracts are deployed on each supported chain and are responsible for minting and burning USDC. - -As a modular component of Synapse's router system, CCTP can be configured to bridge through any supported liquidity source, such as [Curve](https://github.com/synapsecns/synapse-contracts/blob/885cbe06a960591b1bdef330f3d3d57c49dba8e2/contracts/router/modules/pool/curve/CurveV1Module.sol), [Algebra](https://github.com/synapsecns/synapse-contracts/blob/885cbe06a960591b1bdef330f3d3d57c49dba8e2/contracts/router/modules/pool/algebra/AlgebraModule.sol), [DAI PSM](https://github.com/synapsecns/synapse-contracts/blob/885cbe06a960591b1bdef330f3d3d57c49dba8e2/contracts/router/modules/pool/dss/DssPsmModule.sol), and others. diff --git a/docs/bridge/docs/CCTP/Relayer.md b/docs/bridge/docs/CCTP/Relayer.md deleted file mode 100644 index 35a309cbfc..0000000000 --- a/docs/bridge/docs/CCTP/Relayer.md +++ /dev/null @@ -1,107 +0,0 @@ -# CCTP Relayer - -The CCTP relayer is an off-chain service aimed at fulfilling transactions requested through the [CCTP Contracts](./Contracts.md). The relayer is responsible for fetching attestations from the [Circle API](https://developers.circle.com/stablecoin/reference) and submitting them to the CCTP contracts. Anyone can run a relayer. - -### Architecture - -The relayer is a Golang application that polls for events on chain and uses a combo state (db status) and event (on-chain logs) driven [architecture](https://medium.com/@matt.denobrega/state-vs-event-based-web-architectures-59ab1f47656b) to process transactions. The relayer is designed to be run by anyone and be easily observable. - -At a high level, the relayer works like this: - -1. Poll for new transactions from the CCTP contracts and add them to the database as [Pending](https://pkg.go.dev/github.com/synapsecns/sanguine/services/cctp-relayer@v0.10.0/types#MessageState) -2. Fetch the attestation from the Circle API. Once successful add attestation to the database and update status to be [Attested](https://pkg.go.dev/github.com/synapsecns/sanguine/services/cctp-relayer@v0.10.0/types#MessageState) -3. Submit the attestation to the CCTP contracts. Once the transaction has been added to [Submitter](../Services/Submitter#Observability), mark as [Submitted](https://pkg.go.dev/github.com/synapsecns/sanguine/services/cctp-relayer@v0.10.0/types#MessageState) -4. Poll for the transaction receipt and mark as [Confirmed](https://pkg.go.dev/github.com/synapsecns/sanguine/services/cctp-relayer@v0.10.0/types#MessageState) - -### Modes - -As specified by the [cctp_type](#Configuration), the CCTP relayer can be run in one of two modes. In [Synapse mode](https://pkg.go.dev/github.com/synapsecns/sanguine/services/cctp-relayer@v0.10.0/types#MessageType), the [Synapse CCTP](./Contracts.md)contracts are listened to and events relayed through there (including metadata). In [Circle Mode](https://pkg.go.dev/github.com/synapsecns/sanguine/services/cctp-relayer@v0.10.0/types#MessageType), raw [TokenMessenger](https://github.com/circlefin/evm-cctp-contracts/blob/817397db0a12963accc08ff86065491577bbc0e5/src/TokenMessenger.sol) events are relayed. This mode can only be used for USDC to USDC bridges and is not commonly used. - -## Running the Relayer - -### Building From Source - -To build the CCTP Relayer from source, you will need to clone the repository and run the main.go file with the config file. Building from source requires go 1.21 or higher and is generally not recommended for end-users. - -1. `git clone https://github.com/synapsecns/sanguine --recursive` -2. `cd sanguine/services/cctp-relayer` -3. `go run main.go --config /path/to/config.yaml` - -### Running the Docker Image - -The CCTP Relayer can also be run with docker. To do this, you will need to pull the [docker image](https://github.com/synapsecns/sanguine/pkgs/container/sanguine%2Fcctp-relayer) and run it with the config file: - -```bash -docker run ghcr.io/synapsecns/sanguine/cctp-relayer:latest --config /path/to/config -``` - -There is also a helm chart available for the CCTP Relayer [here](https://artifacthub.io/packages/helm/synapse/cctp/0.2.0), but it is recommended you create your own. - -### Configuration - -The CCTP Relayer is configured with a yaml file. The following is an example configuration: - -
- example config -```yaml - cctp_type: "synapse" - # prod contract addresses - chains: - - chain_id: 1 - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" - - chain_id: 43114 - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" - - chain_id: 42161 - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" - - chain_id: 10 - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" - - chain_id: 8453 - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" - - chain_id: 137 - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" - base_omnirpc_url: "http://omnrpc-url/" - unbonded_signer: - type: "AWS" - # should be a mounted secret - file: "/config/aws.txt" - http_backoff_initial_interval_ms: 1000 - http_backoff_max_elapsed_time_ms: 300000 - # submitter config for cctp - submitter_config: - chains: - 1: - supports_eip_1559: true - gas_estimate: 1000000 - 42161: - gas_estimate: 30000000 - max_gas_price: 10000000000 - supports_eip_1559: true - 43114: - gas_estimate: 5000000 - max_gas_price: 1000000000000 - supports_eip_1559: true - 10: - gas_estimate: 5000000 - max_gas_price: 2000000000 - supports_eip_1559: true - 8453: - gas_estimate: 5000000 - 137: - gas_estimate: 5000000 - max_gas_price: 10000000000000 - supports_eip_1559: true -``` -
- - - `cctp_type`: The type of CCTP to listen to. Can be either `synapse` or `circle`. - - `chains`: A list of chain ids and their corresponding CCTP contract addresses. If synapse mode, this should be `synapse_cctp_address` and if circle mode, this should be `token_messenger_address`. Both modes cannot be used at once and the other will be ignored if both are set. - - `base_omnirpc_url`: The base URL for the OmniRPC service. - - `unbonded_signer`: The signer to use for transactions. Can be either `AWS`, `File` or `GCP`. The file should be a mounted secret. More details can be found [here](../Services/Signer). - - `port`: The port to run the relayer on (e.g. 8080) - - `host`: The host to run the relayer on (e.g. localhost). Note: this should not be publicly exposed - - `http_backoff_initial_interval_ms`: The initial interval for the backoff in milliseconds. - - `retry_interval_ms`: The interval to wait between attestation request retries in milliseconds. The [CCTP API Rate Limit](https://developers.circle.com/stablecoins/docs/limits) should be kept in mind. - -### Observability - -The CCTP relayer implements open telemetry for both tracing and metrics. Please see the [Observability](../Observability) page for more info. We'd also highly recommend setting up the [submitter dashboard](../Services/Submitter) as well. diff --git a/docs/bridge/docs/CCTP/_category_.json b/docs/bridge/docs/CCTP/_category_.json deleted file mode 100644 index eda3422018..0000000000 --- a/docs/bridge/docs/CCTP/_category_.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "label": "CCTP", - "position": 3, - "link": { - "type": "doc", - "id": "CCTP/Overview" - } -} diff --git a/docs/bridge/docs/rfq/API/sidebar.ts b/docs/bridge/docs/rfq/API/sidebar.ts deleted file mode 100644 index 3c0e12fc63..0000000000 --- a/docs/bridge/docs/rfq/API/sidebar.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { SidebarsConfig } from "@docusaurus/plugin-content-docs"; - -const sidebar: SidebarsConfig = { - apisidebar: [ - { - type: "category", - label: "quotes", - items: [ - { - type: "doc", - id: "rfq/API/get-quotes", - label: "Get quotes", - className: "api-method get", - }, - { - type: "doc", - id: "rfq/API/upsert-quote", - label: "Upsert quote", - className: "api-method put", - }, - ], - }, - ], -}; - -export default sidebar.apisidebar; diff --git a/docs/bridge/docs/rfq/Contracts.md b/docs/bridge/docs/rfq/Contracts.md deleted file mode 100644 index a5b0947942..0000000000 --- a/docs/bridge/docs/rfq/Contracts.md +++ /dev/null @@ -1,51 +0,0 @@ -### Synapse RFQ - -The Synapse RFQ contract source code can be found [here](https://github.com/synapsecns/sanguine/tree/master/packages/contracts-rfq) along with generated documentation [here](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html) - -| Chain | Address | -| -------- | -------------------------------------------------------------------------------------------------------------------------------- | -| Arbitrum | [0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E](https://arbiscan.io/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | -| Base | [0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E](https://basescan.org/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | -| Ethereum | [0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E](https://etherscan.io/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | -| Optimism | [0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E](https://optimistic.etherscan.io/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | -| Scroll | [0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E](https://scrollscan.com/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | -| Linea | [0x34F52752975222d5994C206cE08C1d5B329f24dD](https://lineascan.build/address/0x34F52752975222d5994C206cE08C1d5B329f24dD) | -| BNB Chain| [0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E](https://bscscan.com/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | -| Blast | [0x34F52752975222d5994C206cE08C1d5B329f24dD](https://blastscan.io/address/0x34F52752975222d5994C206cE08C1d5B329f24dD) | - -### On-Chain Architecture & Transaction Flow - -The RFQ contract allows users to post bridge requests based on quotes they have received from the solvers. At a high level, the contract works as follows: - -1. **User calls bridge**: The user calls the bridge contract with the quote they have received from the RFQ API and passing in origin, destination and other paramaters as a [BridgeParam](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgeparams). -2. **Bridge emits event**: The bridge contract emits a [`BridgeRequested`](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgerequested) event. -3. **Solver relays request**: The solver relays the request by calling the [`relay`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#relay) function on the RFQ contract. The contract then pulls the tokens from the solvers wallet (or [msg.value](https://ethereum.stackexchange.com/questions/43362/what-is-msg-value) in the case of eth) and sends them to the user filling their order. -4. **Solver Calls Prove**: The solver then calls the [`prove`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#prove) function on the RFQ contract to prove they have filled the order. In the current implementation, the function must be called from the solver address. -5. **User Claims**: If the solver does not call prove within the optimistic window, the user can call the [`claim`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#claim) function to claim their funds back. - -### On-Chain Statuses - -Like the relayer, each transaction in the RFQ contract has a status. The statuses are as follows: - -| Status | Int | Meaning | -|-----------------|-----|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Null | 0 | Bridge transaction doesn't exist yet on the origin chain. | -| Requested | 1 | A bridge has been requested, but the [`prove`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#prove) has not yet been called | -| Relayer Proved | 2 | The relayer has tried to [`prove`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#prove) the transaction, but cannot claim yet. | -| Relayer Claimed | 3 | The relayer has called [`claim`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#claim) and gotten the funds. | -| Refunded | 4 | The relayer has not called `claim` within the optimistic period or a dispute has been decided in favor of the user and the users been refunded. | - - -### Dispute Period and Guards - -The RFQ system includes a dispute period and guards to ensure the integrity of bridge transactions. Here's how it works: - -After a relayer submits a proof for a bridge transaction, there's a set period during which the transaction can be disputed. This period allows for detection and correction of any errors or fraudulent activities. Guards are responsible for monitoring bridge transactions and initiating disputes if they detect issues such as incorrect fill amounts and proofs submitted by the wrong relayer. A successful dispute would end up with the relayer losing their claimable funds. - -The current implementation is architectured to enforce honest behavior and also protect honest relayers in cases of blockchain reorgs. - -### Other Functionalities - -**ChainGas** - -`sendChainGas` is a field that is populated by the bridge user, and it's a simple bool flag. If `sendChainGas=true` the amount is specified in the FastBridge contract on the destination chain as `chainGasAmount`. This is currently set to zero in all the contracts, and can thus be ignored by filling orders with either no `sendChainGas` option (or to chains with `chainGasAmount==0`) diff --git a/docs/bridge/docs/rfq/RFQ.md b/docs/bridge/docs/rfq/RFQ.md deleted file mode 100644 index d2d9aaf7bd..0000000000 --- a/docs/bridge/docs/rfq/RFQ.md +++ /dev/null @@ -1,24 +0,0 @@ -# RFQ - -RFQ is a bridge module supported by the Synapse Router that allows different market makers to post quotes on different bridge routes. Users can take these quotes by submitting an on-chain bridge request. In the event these requests are not fulfilled, users can request a refund after a set period of time. - -### Actors - -With the exception of the smart contract itself, RFQ is agnostic to how users receive quotes and where Solvers choose to post quotes. Below, we explain who the general actors interacting with the contract are and then explain the canonical RFQ implementation. - -- **Solvers -** Solvers (also known as fillers) are market makers that provide liquidity & post quotes to the API. They are then in charge of fulfilling requests on-chain. -- **Users** - End users observe quotes from solvers and post requests on chain. In the event these requests cannot be fulfilled, the user can reclaim their funds after the optimistic window has passed. -- **Quoter -** The quoter runs a service ran by different interfaces to the Synapse Bridge that allows market makers to post quotes and users to read them. The spec of RFQ does not require this to be an “API” in the traditional sense. Interfaces can use protocols like libp2p, irc and dht’s to communicate quotes. - -Right now, RFQ consists of three-different components, with each of the two off-chain components being ran by different actors: - -- **[API](./API) -** The RFQ api is an off-chain service ran by Quoters. user-interfaces that allows market makers/solvers to post quotes on different bridge routes. Solvers that have registered with the FastBridge contract can sign messages that post quotes signifying at what price they are willing to bridge tokens on a certain route. - - In the canonical implementation, users Solvers authenticated by signing requests with their private key in accordance with [EIP-191](https://eips.ethereum.org/EIPS/eip-191). The canonical implementation can be found [here](https://github.com/synapsecns/sanguine/tree/master/services/rfq). -- **Fast Bridge Contract -** The fast bridge contract is the core of the RFQ protocol and what allows solvers to fulfill requests from users. A user deposits their funds into the FastBridge contract along with the lowest price they are willing to accept for a given route (a price they get by reading quotes from the Quoter). - - In the unlikely event no Solver is available to fulfill a users request, a user can permissionlessly claim their funds back after waiting an optimistic period. - - Contract code level documentation can be found [here](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html). -- **Relayer** - The relayer is a service ran by the solvers. The relayer is responsible for posting quotes & fulfilling requests. While the relayer can be implemented in any way, the canonical implementation is a golang based relayer that provides a way to decide what chains/routes to quote on, how much to quote and which addresses not to relay for. - diff --git a/docs/bridge/docs/rfq/Relayer/Relayer.md b/docs/bridge/docs/rfq/Relayer/Relayer.md deleted file mode 100644 index b47e066ea6..0000000000 --- a/docs/bridge/docs/rfq/Relayer/Relayer.md +++ /dev/null @@ -1,282 +0,0 @@ ---- -sidebar_position: 0 -sidebar_label: Relayer ---- -:::note - -Relayers must be whitelisted in order to fill bridgeRequests. - -::: - -At a high level, the canonical implementation of the relayer has 3 different responsibilities. - -- **Quoting** - Keep track of balances on each chain as well as in-flight funds and continuously post-quotes with these balances using the config to adjust quotes to the solvers specifications and posting to the API. -- **Relaying -** Fulfill users [BridgeRequests](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgerequested) by relaying their funds on-chain. Once eligible, claim the users funds on the origin chain. -- **Rebalancing -** In order to handle the complexity of user flows, the Relayer provides an interface that allows funds to be rebalanced. This allows RFQ to be reflexive to cases where flows are mono-directional. - -## Architecture - -The relayer is a Golang application that polls for events on chain and uses a combo state (db status) and event (on-chain logs) driven architecture to process transactions. The relayer has 3 different event loops going at any given time, specified above and elaborated on below: - -### Quoting -The quoting loop is comparitively simple and updates the api on each route it supports. Quotes are posted using the following formula: - - - **Do not quote above available balance**: Available balance is determined by `balance on chain - in-flight funds`. If the token is the gas token, then the minimum gas token amount is subtracted. The relayer will also not post quotes below the `min_quote_amount` specified in the config. - - **Quote offset**: The quote offset is a percentage of the price of the token. This is used to ensure that the relayer is profitable. The quote offset is added to the price of the token to determine the quote price. - - **Fee**: The fee is determined by the `fixed_fee_multiplier` in the config. This is multiplied by the `origin_gas_estimate` and `destination_gas_estimate` to determine the fee. This fee is added to the quote price. - -### Rebalancing - -Automated rebalancing is configurable by token and currently supports CCTP routes as well as native bridging in the case of Scroll. Rebalancing evaluation follows this logic on every `rebalance_interval`: - -- For every supported rebalance method, compute the 'total balance' of each token as the sum of all balances across chains that support the given rebalance method. -- For every supported token, compute the 'maintenance threshold' as the product of the `maintenance_balance_pct` and the 'total balance'. A token is eligible for rebalancing if the current balance is below the 'maintenance threshold'. - -To limit the amount of inventory that is inflight, only one rebalance can be pending at a time for each token. As such, we select the 'best' rebalance candidate as the rebalance with the largest delta between origin balance and destination balance. - -The final step in evaluating a rebalance is determining the rebalance amount: - -- Arrive at an initial rebalance amount by computing the delta between current balance and the initial threshold on origin. -- Check if this rebalance amount is too much, i.e. it would take the destination balance above its initial threshold. If so, clip the rebalance amount to target the destination initial threshold. -- Filter the rebalance amount by the configured min and max. - -An example of this process is given below: - -We are dealing with chains 1, 2, and 3. Chains 1 and 2 support USDC with CCTP, and chain 3 supports USDC but does not support CCTP. Each chain has a `maintenance_pct` of 20%, while chains 1 and 2 have `initial_pct` of 40% and chain 3 has 20%. Assume chain 1 has a balance of 100 USDC, chain 2 has 900 USDC, and chain 3 has 2000 USDC. - -The system would trigger a rebalance from chain 2 to chain 1, since the total balance of CCTP-supported chains is 1000 USDC, and chain 1 is below the 20% maintenance threshold. Chain 3 is not considered for rebalance since it does not support CCTP. - -The rebalance amount would be initially be targeted as 600 since this would take chain 2 to its 40% initial threshold. However, since chain 1 only needs 300 to reach its initial 40% threshold, the rebalance amount is clipped at 300. - -So, the final result is a rebalance of 300 USDC from chain 2 to chain 1, leading to chain 1 having 400 USDC, chain 2 having 600 USDC, and chain 3 having 2000 USDC. - - -1. At `rebalance_interval`, check the `maintenance_balance_pct` of each token on each chain and compare it to the current balance. If the balance is below the `maintenance_balance_pct`, continue -2. Calculate the amount to rebalance by taking the difference between the maintenance balance and the current balance and multiplying it by the `initial_balance_pct`. -3. If the amount to rebalance is greater than the `max_rebalance_amount`, set the amount to rebalance to the `max_rebalance_amount`. If the amount to rebalance is less than the `min_rebalance_amount`, do not rebalance. -4. Repeat after `rebalance_interval` - -The implementation for certain native bridges (e.g Scroll) is also supported. It works slightly differently as flows are only supported between Scroll and Mainnet. At a high level, the rebalancer checks inventory on Scroll versus other chains, and if imbalanced, initiates a bridge to mainnet, allowing the CCTP relayer to rebalance funds where needed. - - -### Relaying - -The relaying loop is the most complex and is responsible for relaying funds on-chain. The relayer listens to events on-chain and status updates in the database to take move transactions through the states. The states are as follows: - -1. An on-chain transaction emits the event [`BridgeRequested`](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgerequested). We store this event in the db with the status [`Seen`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#Seen). -1. Check if the request is valid, If not, it is marked as [`WillNotProcess`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#WillNotProcess) -1. Check if there's enough inventory, if not mark as [`NotEnoughInventory`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#NotEnoughInventory) and try again later. -1. If these checks pass, it's stored as [`CommittedPending`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#CommittedPending). This will automatically reduce the next quote amount posted to the api since the relayers liquidity has been committed. -1. Check the chain to see if transaction is finalized yet, if not wait until it is. -1. Once the transaction is finalized on chain, update the status to [`CommitedConfirmed`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#CommitedConfirmed). This means the transaction is finalized on chain and we can now relay it to the destination chain. -1. Call [`relay`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#relay) on the contract to relay the transaction and update the status to [`RelayPending`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#RelayPending) -1. Listen for the relay in the logs. Once we get it we mark the transaction as [`RelayComplete`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#RelayComplete) -1. Call [`Prove()`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#prove) on the contract to prove that we relayed the transaction. Once this is done, we mark the transaction as [`ProvePosting`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#ProvePosting) -1. Wait for the dispute period to expire. Once it does call [`claim`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#claim) mark the transaction as [`ClaimPending`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#ClaimPending) -1. Wait for the dispute period to expire. Once it does mark the transaction as [`ClaimComplete`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#ClaimComplete) - -## Running a Relayer - -### Building From Source - -To build the relayer from source, you will need to have Go installed. You can install Go by following the instructions [here](https://golang.org/doc/install). Once you have Go installed, you can build the relayer by running the following commands: - -1. `git clone https://github.com/synapsecns/sanguine --recursive` -2. `cd sanguine/services/rfq/relayer` -3. `go run main.go --config /path/to/config.yaml` - -### Running the Docker Image - -The relayer can also be run with docker. To do this, you will need to pull the [docker image](https://github.com/synapsecns/sanguine/pkgs/container/sanguine%2Frfq-relayer) and run it with the config file: - -```bash -docker run ghcr.io/synapsecns/sanguine/rfq-relayer:latest --config /path/to/config -``` - -### Withdrawals - -The `POST /withdraw` endpoint is exposed to allow for withdrawing from the relayer wallet without having to deal with the private key directly. This can be used for manual rebalancing, if desired. To use this feature, the following config values must be set: - -```yaml -enable_api_withdrawals: true -withdrawal_whitelist: - - -``` - -The relayer CLI (at `services/rfq/relayer/main.go`) exposes a withdrawal command for convenience: - -```bash -go run main.go withdraw --relayer-url https://localhost:8081 --chain-id 1 --amount 1000000000000000000 --token-address 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE --to 0x0000000000000000000000000000000000000000 -``` - -Be sure to sub in your respective `to` address! - -### Configuration - -The relayer is configured with a yaml file. The following is an example configuration: - -
- example config - ```yaml - submitter_config: # please see the more detailed submitter documentation - chains: - 1: - supports_eip_1559: true - gas_estimate: 1000000 - database: - type: sqlite # can be other mysql or sqlite - dsn: /tmp/db # should be the dsn of your database. If using sqlite, this can be a path - - signer: # please see more detailed signer config #can be text, gcp, or aws - type: GCP - file: /config/signer.txt - - screener_api_url: 'http://screener-url' # can be left blank - rfq_url: 'http://rfq-api' # url of the rfq api backend. - omnirpc_url: 'http://omnirpc' # url of the omnirpc instance, please reference the Omnirpc section under Services for proper configuration - rebalance_interval: 2m # how often to rebalance - relayer_api_port: '8081' # api port for the relayer api - volume_limit: 10000 # USD price cap for a bridge under block confirmation minimum (configurable under `chains`) - - base_chain_config: # this is hte base chain config, other chains override it - confirmations: 0 - # Claim (72.5k) + Prove (57.5k) gas limits, rounded up - origin_gas_estimate: 130_000 - # Relay gas limit, rounded up - dest_gas_estimate: 110_000 - quote_offset_bps: 2 - native_token: ETH - quote_pct: 90 - min_gas_token: 1000000000000000000 - fixed_fee_multiplier: 1.25 - - chains: - 1: - rfq_address: "0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E" # rfq contract address on eth - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" # ccctp contract address on eth - token_messenger_address: "0xbd3fa81b58ba92a82136038b25adec7066af3155" # token messenger address on eth, note: only one of token_messenger_address or synapse_cctp_address actually needs to be present - cctp_start_block: 19341000 - confirmations: 2 - tokens: - USDC: - address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" - decimals: 6 - price_usd: 1.0 - min_quote_amount: 10000 - rebalance_method: "circlecctp" - maintenance_balance_pct: 20 - initial_balance_pct: 50 - max_rebalance_amount: 500000 - ETH: - address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" - decimals: 18 - price_usd: 2600 - 10: - rfq_address: "0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E" - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" - token_messenger_address: "0x2B4069517957735bE00ceE0fadAE88a26365528f" - cctp_start_block: 116855000 - l1_fee_chain_id: 1 - # Prove + Claim L1 gas estimate - l1_fee_origin_gas_estimate: 20 - # Relay L1 gas estimate - l1_fee_dest_gas_estimate: 10 - tokens: - USDC: - address: "0x0b2c639c533813f4aa9d7837caf62653d097ff85" - decimals: 6 - price_usd: 1.0 - min_quote_amount: 10000 - rebalance_method: "circlecctp" - maintenance_balance_pct: 20 - initial_balance_pct: 50 - max_rebalance_amount: 500000 - ETH: - address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" - decimals: 18 - price_usd: 2600 - - quotable_tokens: - 10-0x0b2c639c533813f4aa9d7837caf62653d097ff85: - - "1-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" - 1-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48: - - "10-0x0b2c639c533813f4aa9d7837caf62653d097ff85" - 1-0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE: - - "10-0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" - 10-0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE: - - "1-0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" - - fee_pricer: - gas_price_cache_ttl: 60 - token_price_cache_ttl: 60 - - cctp_relayer_config: - cctp_type: "circle" - circle_api_url: "https://iris-api.circle.com/v1/attestations" - chains: - - chain_id: 1 - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" - token_messenger_address: "0xbd3fa81b58ba92a82136038b25adec7066af3155" - - chain_id: 10 - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" - token_messenger_address: "0x2B4069517957735bE00ceE0fadAE88a26365528f" - base_omnirpc_url: "http://omnirpc" # Make sure this is configured properly - unbonded_signer: - type: GCP - file: /config/signer.txt - http_backoff_initial_interval_ms: 1000 - http_backoff_max_elapsed_time_ms: 300000 - ``` -
- - - - - `submitter_config` - This is covered [here](../../Services/Submitter#Observability). At a high level this controls gas parameters used for on-chain transactions. - - `database` - The database settings for the API backend. A database is required to store quotes and other information. Using SQLite with a dsn set to a `/tmp/` directory is recommended for development. - - `type` - the database driver to use, can be `mysql` or `sqlite`. - - `dsn` - the dsn of your database. If using sqlite, this can be a path, if using mysql please see [here](https://dev.mysql.com/doc/connector-odbc/en/connector-odbc-configuration.html) for more information. - - `screener_api_url` (optional) - Please see [here](https://github.com/synapsecns/sanguine/tree/master/contrib/screener-api#screening-api) for an api spec, this is used descision on wether to bridge to given addresses. - - `rfq_url` - URL of the rfq api, please see the [API](../API#api-urls) page for details and the mainnet/testnet urls. - - `omnirpc_url` - URL of omnirpc to use, Please see [here](../../Services/Omnirpc) for details on running an omnirpc instance. - - `rebalance_interval` - How often to rebalance, formatted as (s = seconds, m = minutes, h = hours) - - `relayer_api_port` - the relayer api is used to control the relayer. This api should be secured/not public. - - `base_chain_config`: Base chain config is the default config applied for each chain if the other chains do not override it. This is covered in the chains section. - - `enable_guard` - Run a guard on the same instance. - - `submit_single_quotes` - Wether to use the batch endpoint for posting quotes to the api. This can be useful for debugging. - - `chains` - each chain has a different config that overrides base_chain_config. Here are the parameters for each chain - - `rfq_address` - the address of the rfq contract on this chain. These addresses are available [here](../Contracts.md). - - - `synapse_cctp_address` (optional) - this is only applicable if **rebalance_method** is set to synapse. This is the address of the CCTP contract available [here](../../CCTP/Contracts). - - `token_messenger_address` (optional) - this is only applicable if **rebalance_method** is set to cctp. Tells the relayer to use the token messenger instead of synapse. - - - `confirmations` - how many confirmations to wait before acting on an event. This will vary per-chain. - - `tokens` - this is a map of token symbol→token info for this chain. For example, token may be USDC, ETH, etc - - `address` - address of the token on this chain id - - `decimals` - number of decimals this token uses. Please verify this against the token contract itself. - - `min_quote_amount` - smallest amount to quote for a given chain. This should be balanced against expected gas spend for a relayer to be profitable. `min_quote_amount` is to be given in decimal units (so 1000.00 is 1000) - - `rebalance_method` - rebalance method for this particular kind of token. Some tokens may not have a rebalance method. This is either cctp or token messenger. - - `maintenance_balance_pct` - percent of liquidity that should be maintained on the given chain for this token. If the balance is under this amount a rebalance is triggered. - - `initial_balance_pct` - percent of liquidity to maintain after a rebalance. The total of these on all-chains should be 100. - - `min_rebalance_amount` - amount of this token to try to rebalance - - `max_rebalance_amount` - maximum amount of this token to try to rebalance at once - - `quotable_tokens`: -- `quotable_tokens`: - list of [chain-id]_[token_address]: [chain-id]_[token_address]. For example 1-0x00…. could be paired with 10-0x01 - ```yaml - "1-0x00": - - "1-0x01" - ``` -- `cctp_relayer_config`: See the [CCTP page](../../CCTP/Relayer) - -### Observability - -The RFQ relayer implements open telemetry for both tracing and metrics. Please see the [Observability](../../Observability) page for more info. There is also a custom [grafana dashboard](https://github.com/synapsecns/sanguine/tree/master/services/rfq/relayer/dashboards/dashboard.json) available for the relayer. We'd also highly recommend setting up the [submitter dashboard](../../Services/Submitter) as well. - -![Relayer Grafana Dashboard](dashboard.png) - -The metrics exposed by the relayer are: - -- `inventory_balance`: The balance of the inventory on the chain for a given `token_name` and `relayer`. -- `quote_amount`: The amount quoted for a given `token_name` and `relayer`. -- `status_count`: The distribution of non-terminal `QuoteRequestStatus` values over time. diff --git a/docs/bridge/docs/rfq/_category_.json b/docs/bridge/docs/rfq/_category_.json deleted file mode 100644 index 8097372c06..0000000000 --- a/docs/bridge/docs/rfq/_category_.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "label": "RFQ", - "position": 3, - "link": { - "type": "doc", - "id": "rfq/RFQ" - } -} diff --git a/docs/bridge/docusaurus.config.ts b/docs/bridge/docusaurus.config.ts index d7ed74fca7..9e7b597094 100644 --- a/docs/bridge/docusaurus.config.ts +++ b/docs/bridge/docusaurus.config.ts @@ -1,13 +1,32 @@ -import {themes as prismThemes} from 'prism-react-renderer'; -import type {Config} from '@docusaurus/types'; -import type * as Preset from '@docusaurus/preset-classic'; -import * as path from "path"; -import {codecovWebpackPlugin} from "@codecov/webpack-plugin"; +/* eslint-disable prefer-arrow/prefer-arrow-functions */ +import * as path from 'path' + +import { themes as prismThemes } from 'prism-react-renderer' +import type { Config } from '@docusaurus/types' +import type * as Preset from '@docusaurus/preset-classic' +import { codecovWebpackPlugin } from '@codecov/webpack-plugin' + +const options = { + id: 'api', // plugin id + docsPluginId: 'classic', // id of plugin-content-docs or preset for rendering docs + config: { + rfqapi: { + // the referenced when running CLI commands + specPath: '../../services/rfq/api/docs/swagger.yaml', // path to OpenAPI spec, URLs supported + baseUrl: 'https://rfq-api.omnirpc.io/', + outputDir: 'docs/rfq/API', // output directory for generated files + sidebarOptions: { + // optional, instructs plugin to generate sidebar.js + groupPathsBy: 'tag', // group sidebar items by operation "tag" + }, + }, + }, +} const config: Config = { title: 'Synapse Bridge Docs', tagline: 'The future is cross-chain.', - favicon: 'img/favicon.ico', + favicon: 'brand-assets/synapse-mark.svg', // Set the production url of your site here url: 'https://docs.bridge.synapseprotocol.com', @@ -41,8 +60,9 @@ const config: Config = { // Remove this to remove the "edit this page" links. editUrl: 'https://github.com/synapsecns/sanguine/tree/master/docs/bridge/', - // docLayoutComponent: "@theme/DocPage", - docItemComponent: "@theme/ApiItem" // derived from docusaurus-theme-openapi-docs + docRootComponent: '@theme/DocRoot', + docItemComponent: '@theme/ApiItem', // derived from docusaurus-theme-openapi-docs + // docItemComponent: '@theme/ApiItem', // derived from docusaurus-theme-openapi-docs }, theme: { customCss: './src/css/custom.css', @@ -58,13 +78,13 @@ const config: Config = { announcementBar: { id: 'announcementBar-v3.2', // Increment on change // content: `⭐️ If you like Docusaurus, give it a star on GitHub and follow us on Twitter ${TwitterSvg}`, - content: `⚠️️ Caution! These docs are a work in progress. Informaton may be incorrect or incomplete. For the current docs, please see here `, + content: `⚠️️ Caution! These docs are a work in progress. Information may be incorrect or incomplete. For the current docs, please see here `, }, navbar: { title: 'Synapse Bridge Docs', logo: { - alt: 'My Site Logo', - src: 'img/logo.svg', + alt: 'Synapse logo mark', + src: 'brand-assets/synapse-mark.svg', }, items: [ { @@ -75,7 +95,7 @@ const config: Config = { ], }, footer: { - style: 'dark', + // style: 'dark', links: [ // { // title: 'Docs', @@ -120,54 +140,42 @@ const config: Config = { darkTheme: prismThemes.dracula, }, } satisfies Preset.ThemeConfig, - themes: ["docusaurus-theme-openapi-docs"], // export theme components + themes: ['docusaurus-theme-openapi-docs'], // export theme components plugins: [ - [ - 'docusaurus-plugin-openapi-docs', - { - id: "api", // plugin id - docsPluginId: "classic", // id of plugin-content-docs or preset for rendering docs - config: { - rfqapi: { // the referenced when running CLI commands - specPath: "../../services/rfq/api/docs/swagger.yaml", // path to OpenAPI spec, URLs supported - baseUrl: "https://rfq-api.omnirpc.io/", - outputDir: "docs/rfq/API", // output directory for generated files - sidebarOptions: { // optional, instructs plugin to generate sidebar.js - groupPathsBy: "tag", // group sidebar items by operation "tag" - }, - }, - } - }, - ], + ['docusaurus-plugin-openapi-docs', options], // please see: https://github.com/facebook/docusaurus/issues/8091#issuecomment-1269112001 for an explanation. - () => ({ - name: 'resolve-react', - configureWebpack() { - return { - resolve: { - alias: { - // assuming root node_modules is up from "./packages/ - react: path.resolve('../../node_modules/react'), + function () { + return { + name: 'resolve-react', + configureWebpack() { + return { + resolve: { + alias: { + // assuming root node_modules is up from "./packages/ + react: path.resolve('../../node_modules/react'), + }, }, - }, - }; - }, - }), - () => ({ - name: 'bundle-analyzer', - configureWebpack() { - return { - plugins: [ - codecovWebpackPlugin({ - enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, - bundleName: "docs-bridge", - uploadToken: process.env.CODECOV_TOKEN, - }), - ] - }; + } + }, } - }), + }, + function () { + return { + name: 'bundle-analyzer', + configureWebpack() { + return { + plugins: [ + codecovWebpackPlugin({ + enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, + bundleName: 'docs-bridge', + uploadToken: process.env.CODECOV_TOKEN, + }), + ], + } + }, + } + }, ], -}; +} -export default config; +export default config satisfies Config diff --git a/docs/bridge/package.json b/docs/bridge/package.json index 078ad6c137..80dca5e89d 100644 --- a/docs/bridge/package.json +++ b/docs/bridge/package.json @@ -20,26 +20,27 @@ }, "dependencies": { "@codecov/webpack-plugin": "^0.0.1-beta.10", - "@docusaurus/core": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/plugin-content-docs": "3.1.1", - "@docusaurus/preset-classic": "3.1.1", - "@docusaurus/theme-common": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/module-type-aliases": "3.5.2", + "@docusaurus/plugin-content-docs": "3.5.2", + "@docusaurus/preset-classic": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/tsconfig": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", - "docusaurus-plugin-openapi-docs": "3.0.0-beta.10", - "docusaurus-theme-openapi-docs": "3.0.0-beta.10", + "docusaurus-plugin-openapi-docs": "^4.0.1", + "docusaurus-theme-openapi-docs": "^4.0.1", "prism-react-renderer": "^2.3.0", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^18.2.0", + "react-dom": "^18.2.0", + "synapse-constants": "1.3.22" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.2.1", - "@docusaurus/tsconfig": "3.2.1", - "@docusaurus/types": "3.2.1", "typescript": "~5.2.2" }, "browserslist": { diff --git a/docs/bridge/sidebars.ts b/docs/bridge/sidebars.ts index acc7685acd..cf181d50cd 100644 --- a/docs/bridge/sidebars.ts +++ b/docs/bridge/sidebars.ts @@ -1,18 +1,18 @@ -import type {SidebarsConfig} from '@docusaurus/plugin-content-docs'; +import type { SidebarsConfig } from '@docusaurus/plugin-content-docs' /** * Creating a sidebar enables you to: - create an ordered group of docs - render a sidebar for each doc of that group - provide next/previous navigation - + The sidebars can be generated from the filesystem, or explicitly defined here. - + Create as many sidebars as you want. */ const sidebars: SidebarsConfig = { // By default, Docusaurus generates a sidebar from the docs folder structure - tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], + tutorialSidebar: [{ type: 'autogenerated', dirName: '.' }], // But you can create a sidebar manually /* @@ -26,6 +26,6 @@ const sidebars: SidebarsConfig = { }, ], */ -}; +} -export default sidebars; +export default sidebars diff --git a/docs/bridge/src/components/AnimatedLogo.tsx b/docs/bridge/src/components/AnimatedLogo.tsx new file mode 100644 index 0000000000..61bf5dd39e --- /dev/null +++ b/docs/bridge/src/components/AnimatedLogo.tsx @@ -0,0 +1,143 @@ +export default () => { + return ( + + ) +} diff --git a/docs/bridge/src/components/BridgeFlow.tsx b/docs/bridge/src/components/BridgeFlow.tsx new file mode 100644 index 0000000000..f5e2378587 --- /dev/null +++ b/docs/bridge/src/components/BridgeFlow.tsx @@ -0,0 +1,228 @@ +export const BridgeFlow = () => { + return ( + + + + + + + + + + + + + + + + + + originChain + + + destChain + + App / SDK + Wallet + Bridge + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/docs/bridge/src/components/CCTPFlow.tsx b/docs/bridge/src/components/CCTPFlow.tsx new file mode 100644 index 0000000000..4b3daad2fb --- /dev/null +++ b/docs/bridge/src/components/CCTPFlow.tsx @@ -0,0 +1,191 @@ +export const CCTPFlow = () => { + return ( + + + + + + + + + + + + + + + + + + originChain + + + destChain + + App / SDK + Wallet + Circle + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/docs/bridge/src/components/HomepageFeatures/index.tsx b/docs/bridge/src/components/HomepageFeatures/index.tsx index 50a9e6f4c7..de3376181d 100644 --- a/docs/bridge/src/components/HomepageFeatures/index.tsx +++ b/docs/bridge/src/components/HomepageFeatures/index.tsx @@ -1,12 +1,14 @@ -import clsx from 'clsx'; -import Heading from '@theme/Heading'; -import styles from './styles.module.css'; +/* eslint-disable @typescript-eslint/no-var-requires */ +import clsx from 'clsx' +import Heading from '@theme/Heading' + +import styles from './styles.module.css' type FeatureItem = { - title: string; - Svg: React.ComponentType>; - description: JSX.Element; -}; + title: string + Svg: React.ComponentType> + description: JSX.Element +} const FeatureList: FeatureItem[] = [ { @@ -39,9 +41,9 @@ const FeatureList: FeatureItem[] = [ ), }, -]; +] -function Feature({title, Svg, description}: FeatureItem) { +const Feature = ({ title, Svg, description }: FeatureItem) => { return (
@@ -52,10 +54,10 @@ function Feature({title, Svg, description}: FeatureItem) {

{description}

- ); + ) } -export default function HomepageFeatures(): JSX.Element { +export default () => { return (
@@ -66,5 +68,5 @@ export default function HomepageFeatures(): JSX.Element {
- ); + ) } diff --git a/docs/bridge/src/components/RFQFlow.tsx b/docs/bridge/src/components/RFQFlow.tsx new file mode 100644 index 0000000000..5a25396d5d --- /dev/null +++ b/docs/bridge/src/components/RFQFlow.tsx @@ -0,0 +1,185 @@ +export const RFQFlow = () => { + return ( + + + + + + + + + + + + + + + + + + + originChain + + + destChain + + App / SDK + User + Relayer + Bridge + + + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/docs/bridge/src/components/Routes.tsx b/docs/bridge/src/components/Routes.tsx new file mode 100644 index 0000000000..46d0cacdb0 --- /dev/null +++ b/docs/bridge/src/components/Routes.tsx @@ -0,0 +1,51 @@ +import { BRIDGABLE_TOKENS, CHAINS } from 'synapse-constants' + +const CHAINS_BY_ID = {} + +for (const { chainImg, id, name } of Object.values(CHAINS)) { + if (id && name) { + CHAINS_BY_ID[id] = { name, chainImg } + } +} + +export default () => + Object.entries(BRIDGABLE_TOKENS).map(([id, tokens]) => { + const chain = CHAINS_BY_ID[id] + const chainImg = chain.chainImg({ width: 28, height: 28 }) + return ( +
+

+ {chainImg} {chain.name} {id} +

+ {Object.values(tokens).map((token) => { + const tokenImg = + typeof token.icon === 'string' ? ( + + ) : ( + token.icon({ width: 16, height: 16 }) + ) + + return ( + + {tokenImg} {token.symbol} + + ) + })} +
+ ) + }) diff --git a/docs/bridge/src/components/SVGBridge.tsx b/docs/bridge/src/components/SVGBridge.tsx new file mode 100644 index 0000000000..9fdc0049cf --- /dev/null +++ b/docs/bridge/src/components/SVGBridge.tsx @@ -0,0 +1,182 @@ +export default () => { + return ( + + + + + + + + + + + + + + + + + Connect Wallet + + + From + + + Network + + + + + + Out + + + + 0.0000 + + + + + + To + + + Network + + + + + In + + + + 0.0000 + + + + + + Select origin token + + + + + + Bridge + + + ) +} diff --git a/docs/bridge/src/components/SVGWidget.tsx b/docs/bridge/src/components/SVGWidget.tsx new file mode 100644 index 0000000000..f674e3b8ae --- /dev/null +++ b/docs/bridge/src/components/SVGWidget.tsx @@ -0,0 +1,160 @@ +export default () => { + return ( + + + + + + + + + + + + + + + + + Network + + + + + Out + + + + Available 0.0000 + + + 0.0000 + + + + + Network + + + + 0.0000 + + + + In + + + + + + 25 seconds via SynapseRFQ + + + + + Bridge + + + ) +} diff --git a/docs/bridge/src/components/USDC.tsx b/docs/bridge/src/components/USDC.tsx new file mode 100644 index 0000000000..671a85c162 --- /dev/null +++ b/docs/bridge/src/components/USDC.tsx @@ -0,0 +1,7 @@ +import { USDC } from 'synapse-constants' +console.log(0, USDC) + +export const Test = () => { + console.log(1, USDC.icon) + return +} diff --git a/docs/bridge/src/css/custom.css b/docs/bridge/src/css/custom.css index 2bc6a4cfde..ffda217488 100644 --- a/docs/bridge/src/css/custom.css +++ b/docs/bridge/src/css/custom.css @@ -6,25 +6,107 @@ /* You can override the default Infima variables here. */ :root { - --ifm-color-primary: #2e8555; - --ifm-color-primary-dark: #29784c; - --ifm-color-primary-darker: #277148; - --ifm-color-primary-darkest: #205d3b; - --ifm-color-primary-light: #33925d; - --ifm-color-primary-lighter: #359962; - --ifm-color-primary-lightest: #3cad6e; + --ifm-color-primary: hsl(285deg 100% 35%); + --ifm-color-primary-dark: hsl(285deg 100% 32%); + --ifm-color-primary-darker: hsl(285deg 100% 30%); + --ifm-color-primary-darkest: hsl(285deg 100% 25%); + --ifm-color-primary-light: hsl(285deg 100% 39%); + --ifm-color-primary-lighter: hsl(285deg 100% 40%); + --ifm-color-primary-lightest: hsl(285deg 100% 46%); --ifm-code-font-size: 95%; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); + /* --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); */ + + --ifm-color-secondary-contrast-background: hsl(285deg 100% 96%); + --ifm-color-secondary-dark: hsl(285deg 100% 75%); + + --ifm-background-surface-color: #f8f8f8; + + letter-spacing: .0125em; + line-height: 1.7; } /* For readability concerns, you should choose a lighter palette in dark mode. */ -[data-theme='dark'] { - --ifm-color-primary: #25c2a0; - --ifm-color-primary-dark: #21af90; - --ifm-color-primary-darker: #1fa588; - --ifm-color-primary-darkest: #1a8870; - --ifm-color-primary-light: #29d5b0; - --ifm-color-primary-lighter: #32d8b4; - --ifm-color-primary-lightest: #4fddbf; +:root[data-theme='dark'] { + --ifm-color-primary: hsl(285deg 100% 80%); + --ifm-color-primary-dark: hsl(285deg 100% 41%); + --ifm-color-primary-darker: hsl(285deg 100% 38%); + --ifm-color-primary-darkest: hsl(285deg 100% 32%); + --ifm-color-primary-light: hsl(285deg 100% 50%); + --ifm-color-primary-lighter: hsl(285deg 100% 52%); + --ifm-color-primary-lightest: hsl(285deg 100% 59%); --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); + + --ifm-color-gray-700: hsl(240deg 20% 20%); + --ifm-color-gray-900: hsl(240deg 25% 10%); + + --ifm-background-color: hsl(240deg 25% 7.5%); + --ifm-background-surface-color: var(--ifm-color-gray-900); + --ifm-table-stripe-background: var(--ifm-color-gray-900); + + --ifm-color-secondary-contrast-background: hsl(285deg 40% 12.5%); + --ifm-color-secondary-dark: hsl(285deg 40% 25%); + + --ifm-color-success-contrast-background: hsl(165deg 40% 12.5%); + --ifm-color-success-dark: hsl(165deg 40% 25%); +} + +h1 { font-weight: 400 } + +h2 { + font-weight: 600; + border-top: 1px solid hsl(285deg 20% 50% / 20%); + margin-top: 1.5em; + padding-top: 1em; +} + +h3 { + font-size: 1.2rem; + margin: 1.5rem 0 .75rem 0; +} + +ol, ul { margin-bottom: 1.5em } +ol a, ul a { font-weight: 500 } + +table thead tr { + text-align: left; + border: none; +} + +svg.flowAnimation text { + font-family: monospace; +} + +figure { + margin: 0; + width: fit-content; +} + +figcaption { + text-align: center; + font-style: italic; + opacity: .67; + margin: 1rem 0; +} + +#flowGroup { + display: grid; + width: 100%; + gap: 2rem; + margin: 2rem 0; +} + +#flowGroup figure { + width: 100%; + max-width: 40rem; +} + +@media only screen and (min-width: 96rem) { + #flowGroup { + grid-template-columns: 1fr 1fr; + } + + #flowGroup figcaption { + /* margin: .5rem; */ + text-align: center; + } } diff --git a/docs/bridge/src/pages/index.tsx b/docs/bridge/src/pages/index.tsx index 2323a056b4..c9459a0f3a 100644 --- a/docs/bridge/src/pages/index.tsx +++ b/docs/bridge/src/pages/index.tsx @@ -1,47 +1,48 @@ -import clsx from 'clsx'; -import Link from '@docusaurus/Link'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import Layout from '@theme/Layout'; -import HomepageFeatures from '@site/src/components/HomepageFeatures'; -import Heading from '@theme/Heading'; -import { Redirect } from 'react-router-dom'; +// import clsx from 'clsx'; +// import Link from '@docusaurus/Link'; +// import useDocusaurusContext from '@docusaurus/useDocusaurusContext' +// import Layout from '@theme/Layout' +// import HomepageFeatures from '@site/src/components/HomepageFeatures' +// import Heading from '@theme/Heading' +import { Redirect } from '@docusaurus/router' -import styles from './index.module.css'; +// import styles from './index.module.css' -function HomepageHeader() { - const {siteConfig} = useDocusaurusContext(); - return ( -
-
- - {siteConfig.title} - -

{siteConfig.tagline}

-
- - Docusaurus Tutorial - 5min ⏱️ - -
-
-
- ); -} +// const HomepageHeader = () => { +// const { siteConfig } = useDocusaurusContext() +// return ( +//
+//
+// +// {siteConfig.title} +// +//

{siteConfig.tagline}

+//
+// +// Docusaurus Tutorial - 5min ⏱️ +// +//
+//
+//
+// ) +// } -export default function Home(): JSX.Element { - const {siteConfig} = useDocusaurusContext(); - // TODO: a homepage - // for now, just disable entirely: https://v1.docusaurus.io/docs/en/site-creation#docs-landing-page - return ( - - - {/**/} -
- {/**/} -
-
- ); -} +// export default () => { +// const { siteConfig } = useDocusaurusContext() +// // TODO: a homepage +// // for now, just disable entirely: https://v1.docusaurus.io/docs/en/site-creation#docs-landing-page +// return ( +// +// +// {/* +//
+// +//
*/} +//
+// ) +// } + +export default () => diff --git a/docs/bridge/static/brand-assets/synapse-border-mark.svg b/docs/bridge/static/brand-assets/synapse-border-mark.svg new file mode 100644 index 0000000000..9ca9073e8d --- /dev/null +++ b/docs/bridge/static/brand-assets/synapse-border-mark.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/bridge/static/brand-assets/synapse-brand-assets.zip b/docs/bridge/static/brand-assets/synapse-brand-assets.zip new file mode 100644 index 0000000000000000000000000000000000000000..1182d5ba2173d25948fdbf05931750e0f4dbf40e GIT binary patch literal 8516 zcmcI~WmuH$+BMC9fCJLq-Q67n2$Dl1-Q6G|U4tNvbPp*ljkMA!DT;KL(!vYR_dQ$m zIgY)*{T;`9&5vv5&%Ne8&uiAY&$UKH5gq{>25|Rku@TaN`M*m60R{#Sh8)J#+rik$ z)ttq|(Z$T%g~i_3#g^68!%|ZN9R_*j$(aQW?wQ3B&Y6X~*CQBMgdealFn1S&fyq=K zbO>e#ww^J8D=7<+gRD_ya8~0KUsyT*ghfSKLbX$isqYi}=`Rv+w9p#V>J-})(>%Kamr`+qKkM@Fn%BFg z3w?21@c1-hIj?cUv$5DOYUd1lP8~{b|4t-TftQj?LqDo7kXh{$pA^h&*t6MGzw>Uv zHdupXaHACdhvhY0>)@XiZw&j5DHs9#+2YvBz`9!ll!2=P5I1g<2uu7qnepuh%Mv9D zwBXV+vGnrlLf)N3f;~>UBEIG6?8p%TF%z_lfXFaRoJdF2@~>yYGie6syID>Fs7n!x zurcx=rL*pJemgVBq%4@v!JZ|PpDtc8feALkn-sl-jd4(m&I<)BW_81FeJjg_7U1bm zk17=)W`ZAtZUY>lf_aAH-cDyyV^@OBVllLC@9^*rR1}c`eIVNYq`NNN z_gCd#ou{|`)%lNTFfh0eI&bG_>BwSYXKebb@fFzrr}6juAB_*y8g$U(1Ag9MifCaQ zCTI@}O_He}mz=X%&fI}E5Kf~KQz+lITb`?FK%toLMN?LX(zqUTo_%Q-OF!%0I2p7# z!ooeN5fkpn&{C9-zVM&zxb?WK*3EE6Dw7qvapou08a(LlFJ_CrIGQ>7v0&pZklk_P z<9T*-Xx8(gjp<{Ch(L<#ZZaa1+Fr+Ry}m*nneDs9 z%SZuRLlNsKE6eV;8(W7_nO6r_D<;a%$+9svI<%r=f){tT+&fZR_Aj>%Pu&*N>3T5D zIG)Xx2DW!He_1RnYgY8UJ!AcOecKWyXK*u9$qND~!lmay?7#O_hFYAQ_{zFmkR zjv70M?rG~0$JX|Iznv*=LdGyFe)flWMOtsYpf0l}x4P50xlMCc3rACvV5;fo7`)hx zctj0`;&~wsxKkM{+lV9EAC1(F5>VRJe@Ife0~9*3o`a<-d!FdwrU6wIde$+5-vt^A z_@sk8b&hQWz;fu6FYRQ!Dm^-#k~33n1;|88xJNipM`-DYXp{^;5qgA4Q*es{Wq=;! zJ1X(rrolUW;Cech zsQY*mll3~E^Q&n=5MthX8hz?8z`R_teb*VQsStdZP_8H&4;~a(ZIwQhA{)z7qfr3+O%%Yl0QjQ{N9+-z%YP}j(NM6UlfXAISmN+Hj zhNC2pQaL_fY8Q_pr{&Hs__?G+$J1u+LMN&FKpUq*A%o=&$s~jAPPjp|#boe{^BzP7 zh~ZB&Sz$y?ariD4KDAz?vnRX6Lele{qmWtvG@{F*Ny2G}=dv1A4QQ6NSEZL#bwOe& zRAxKE=Nb+tj}?9li6@8h9S6mO#`^IF@loJS03i96r$nD#B~^7(e}oG5_23o&eDbQ$ zOoc;zRf$eU$7%4Y7&&(!^?3BQIJl{)z&0&DJ}8%ZS`Lb^5q&-06efdo%Robd+N$uu zSDxH~4Xrj5BUlZR#vj&vZkx4nxgP8MTJ=&>?Qh}bjmL%wYU%6zRQw&8QSg~_E zyDw>HA;d%TyfZ=h<-j1RQug~ zs~)bR#;n~QoNZ|hM|dB%Mh9732J{WFCoP(SA)l~5?T#dSZf2`_NJIlPh#kzHVr;%< z&rOZv^4#)<%!-?XN%P}=79O)-VOfaUNK+jEf?T2Qa=m?6o?iiC=+K)Go&FHiV|OUy zd#fbD0981Q&#+|ng|DaE8IR!VnWd-XVh=1fg9cfq%k)-?^3-|lQ3{FmH-nv|8pA+l zim7x#qnoIo3uyGcvO>@In6(mq2+kV{HOfn{i4wBK@|5^`w<@AZ=xfO`MgsMiFc;lT1?g zW7EwhWJ>DWv6F0#06jMa=*gtMV$?4q6x9bWciQfci81IKJQQR*8aukMGusm4&!$v|BKHZ z9i$&BdE=du)7~jL<39;K<`<#sA%O65`MW(S%vD*oLu;nHgl3ZM>r2f*Hi_4MqlfXW zU?4IN-0VhthJfq3goXd*aRKW>_{oV$)kpMg?^-_}QMNE*q#x!jP1pYC&G-;b)G`H8 zQ3ubK-YT(OzTnHtQHKCsw9z{iaIBqMa~a1gd)!}4KxAscF5>G`e8?3rE1cCQ_Gr=m zEWKrgtiL6E&msW45Z>!_*?@<3_3hv%`yMNu@|@)%dtcz z^<9o8&R)XTZ8~VTq@HB{B^0;wq5Zy&mNc8CP%q0K)p z&cn(Sn|?b(YAEB}A+mJ9yG_d3!VP^f8{aAgOTmA2q&!C$TgS1rq}s&zNceZ2so4?EFwo(df?UVX{7!PnF;Zo2S?>Q$-+ zyC7k)9k~)M)X3cfE_hvkd_p$AP05c#cMqP4nBgXGlC|4!!omCr$V_WE=*_rJ>w6u6 zEjOof%1%#u!w;dT5+AL3tsA5H^7|7-84kEEgh6QLK`_{QR z1yxnkJ+h#Zv@VNd*UgY_e}o{`Fd(0Kq`+&5upPwv4Jq7(T1SRaBGpaO3ZoPdlD}Hg zRro=L_nl@jx}1QmZz{PJ=&{D;I4UKYRbs8mggb&Uf0t7*tUbIUVBY5omuJ$s zDdU|=RK0#8j%nLx%tQ-JwHNkf@{CU`Yt2BK1DtS+K9KbyB+sifu8GZx^GJvnCu1OC zh#slD`Q8wdwg5O{DmdH55>rJPPHvD2pwUWmane2}j}ps$uMa_?R=(7YWsA@-Fcwg(#H9un~*!^9Oq6orff) z)J2VW3Jo2ZMI=;9Il868H#=**Lpa4kQQmaIHS7CG62BI&v)9#B3xcfiv?6J|xMh3? z$NaJs_MPCx8w__=PVCs-t5Ub9*>~pbC~h?ZCP+;<`AsA#v94~L zzijI|CE`l5gYb~j+@ey_`(r>n17qZg^m2f8XxI-g=gJNf zEIj%I)m9#>@vRyHor1B94%$=j1yGl{+N|+iy9C)UdN*}Hf1u@+|Gte=bNs|~jB&zuM)f6wJ zT$+%{74?Xc(z}qt4l`GX(TTxEo3ElPIO#e!B`DnpJ-6%V1{pn>Fl>QxI)Xd~tte{M z7-@PHNm0aIE3&Q4KXi00z&U&AC-S|h%#^dfJiHT?d+GQkDrbL)3jUu%f}_JzYfCG) zf6a$#cWOd+rz-dRA7jG3st^ePTkpmM9Rv_Y?(FcH{GFkv5{ zFK4{)>iE2e+kI2@1#NqwqvfYHhj7;Dvw#tc(b3V%x$W*F~24QA@sCmFmm$ZumI!ita!MInd#zi`Ls%i zc=@*OMgPYy_N3SZCt$?e)1NsO9h?+b{wCB}+_SEmo6(5kB&w{lc}4gp`iN0p1}*sL3L%C6I!L%^ETtj)1- z!)0>1}N4w)(pmSpw-0>$a&NudmthWpA-Hr^KmZa zGGKokKO{c>5%NsRG^hC-;ckrq3GHxVi{Pp|1~@VVF{nU1&c&K0-ERo>6&4lxkl;Bp zfh$4!oGo4+Dil~%;*YAUYg?~0NthR;zDnLME-M6=k4oWZnutKDF*6SRR3i}`w@fw- zOU6QyL2kGEF=#GAWuB25f=l=M0IoB(!bQ!pHGY^NusJ8Du``!g9{gGXNSg_K6U+#= zOz&Ol#K4(2Y(H(?tvW(TMhhUEfXDHv3j)9st}!RNkx1abVqY{8UwDrd)Ub3mWU%_9 zewVA1H4Wk&)$V576FSgj!Z0mutNj#!hX-Q=9FYGWr7r7b03NYwkTw9Xj2ldu(=^IO zM>kWr&o-BJeL9kT`eXjdVBhJ0o&SPO{6NnfGl5!C4Fu*LX?{~_r$N9s2)7TDKP6Qb zL?H)hIay9kN9Y6&{0fKi$JgR{9fZV$_v6QaPLAZw1xAoWoPR@?O;h){qmZ7Cfr~#+ z0DWSduX?sBAp)YFg9;%m;aoYvO0EQ2NMg6(1diec?YlmWNT_JvcV*BMZka%aKB$Yc z8gcE|SmI-CrIHyU#b+vsHbd;=6h2jUq`5=SjDswyUb3cS0p+NH_UYiHL~>Ja^tZ6n zOC19hFpqL<-d8BM7fEGCqxDi}t+%^$^o6hE&!Il_?^>oroKZn~ye=r!=)Yz>xvYttNvhd-#(d?U&$? za?UF%2q8`3$Kp8$E0;P&_RD%}*K5+-sJ~EikMPXUheaPi1X0m( zgv!&VIRG&ZnYDQ%qS?Md;!H|l^g&5T^2m7F%qgN}WlNCSiP&Tgf_ULz?fqDs+;1hV zRQ2doOt1R}GVUi8Lu-JZ^yVv6{V2juN@a&eGy$81Uum_apiZr~62XOG%MBnNN)T0W z38i%YX8oJguzV%>N}S4R>Nf zeWqux38lEx)j6W%6YAa^v%%*bcJnCGcSP(fRSt@bH#f z4V`25^Nuf7uKP$0DQwwj{-PbeBBH5|Wox{@Sx>Db&$DmmpLL_(Or&8Bv&DuN|I8i@ zFP=WWy79YhOx;L%dltSTR@U1mvA=bJLhI1HbJe}`b7g6f>J@o(Q$HB}JC4Q0Hmkw3 zLG#tE=hjB{0U@dTg;+}wwP9-#J#D;F*36R{7G_!FWHXa0Odm~9XOGp~Nnd|KnJrmu zjLuUR-WDTYN=4SRReKcFJIio<-qJEZr75*7CKCOXMu5Pq%2z|XSs&O)tY#e**9Isa ze;Hk{QlJv;x$^X*&?yRfL`7di^d_ulb~BsL$Wnl5BiX)&0yom03M)K&6hxJO5Y{5QHj0{{hU`uaA}iph6tt?aiA*MNAi% zkE@Y~Vz6)dGwQxLs7N^>qzqEu%oZd6U-X^?rT&BWMEQ2+f6serX;}-xmVN8bNtG4E zm;<|0$KkAq`q9xX1Vkdx-ARsNuU5O=JhZd32IXW;1A}z=-A^u>N32Tn*pNwQmnhIy7L4T_dJNYVo{lNG`@|l|;e`qkbPctNjgwc)1vMI-7kbRYjs5LlY6dLT!Uh z;eD>q*Zd0Znt3gntlZ?#h|A3*19StYra~Wg+eHnxfg+aN#NbDMKnK7KFwzd?MEa1x zAUC}(qtdiNiYG(7-fn#$Q(+Ub$(X{XO|kG2 z=YRjiNQ9iV3Bsii#dFf=D~LW(Sy821**2|}Z(Ga3JD7@;H%jv9%0vqi$L_ z;gzWrfrq(ei)LH~m{bk5)$9&GVHEZnCb^^w<=Ulio89*rGpaCXtWe`Yuy(_DFL=xj zE#gZanlhknD7>k?;aeFj4*1J^I{94k@}J(5(E~m=xJw!yW(oK4(H~g?&RuwP&*%61 zpM3so7$b-st^EpH>7Xjo3Svww>^&C#USQ750RcD6@cB%pK1Fz#Opx4L*8C10iveR z<-x24EVp8!TkboQG;WJkUQP^gD*|Fk9tbW($T-?0T-4b&8_4Ps990b+W?z~IH?o07& zmeTxBMfzRz;O>gUcvqzR{U1dV!sud0`z#Q;)}};{NHOJRWb#r<93_T=T9c4x?Ub6) z1criH@1~VbtYteK7cR36tC0~iu>gEg(jNB-E|`)H6Di)HwT_aF(&qkJ3^&D+#XRHT( zFG#I9k8M5zgg;8yAS{p&9X}3?PWI0t^*tCkHQ-=ifH1%E$lrp2z`>le!x{SbJQtR3W(lh%D_y>e z`ndSzl-FfMLQpz&jdy?-omX;xM) zj^kt$UsbLfica%)nS>5{$t5=Gxu-4x%JB)==45(Qu8!VXjkvyCZ?Sn7su+ajmReN~ zX*UUecCxj5qRRD07VLb-MvXeqr4MuT9i<7{k5X$4GzPt~D6IQdEP0}qs^&uPr=apZ z#ysWgG8udGD~t^*()Ct7-@gti>d(4)%DV%4Uu6|VSU7C>Uy1O;#PB{5{(BV!-oN|* z%!eO>+=q`3ZR~r(_2_*>J%jwc3IgMZf5-UOjPY;yL`4x1 T>HZ4$|Cb + + + + + + + + + + + + + diff --git a/docs/bridge/static/brand-assets/synapse-logo-onDark.svg b/docs/bridge/static/brand-assets/synapse-logo-onDark.svg new file mode 100644 index 0000000000..bcff81820e --- /dev/null +++ b/docs/bridge/static/brand-assets/synapse-logo-onDark.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/bridge/static/brand-assets/synapse-logo-onLight.svg b/docs/bridge/static/brand-assets/synapse-logo-onLight.svg new file mode 100644 index 0000000000..8e2ebc7144 --- /dev/null +++ b/docs/bridge/static/brand-assets/synapse-logo-onLight.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/bridge/static/brand-assets/synapse-logo-white.svg b/docs/bridge/static/brand-assets/synapse-logo-white.svg new file mode 100644 index 0000000000..2bae1b0d48 --- /dev/null +++ b/docs/bridge/static/brand-assets/synapse-logo-white.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/docs/bridge/static/brand-assets/synapse-mark-black.svg b/docs/bridge/static/brand-assets/synapse-mark-black.svg new file mode 100644 index 0000000000..47f379ffa1 --- /dev/null +++ b/docs/bridge/static/brand-assets/synapse-mark-black.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/bridge/static/brand-assets/synapse-mark-white.svg b/docs/bridge/static/brand-assets/synapse-mark-white.svg new file mode 100644 index 0000000000..f9724f7ec7 --- /dev/null +++ b/docs/bridge/static/brand-assets/synapse-mark-white.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/bridge/static/brand-assets/synapse-mark.svg b/docs/bridge/static/brand-assets/synapse-mark.svg new file mode 100644 index 0000000000..7d99490abc --- /dev/null +++ b/docs/bridge/static/brand-assets/synapse-mark.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/docs/bridge/static/img/docusaurus-social-card.jpg b/docs/bridge/static/img/docusaurus-social-card.jpg deleted file mode 100644 index ffcb448210e1a456cb3588ae8b396a597501f187..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55746 zcmbq(by$^M)9+14OPA6h5)#tgAkrW$rF5rshja^@6p-$cZlt9Iq*J;!NH?5&>+^i? zd%l0pA7}Qy_I1b1tTi)h&HByS>tW_$1;CblCG!e^g989K@B=)|13|!}zl4PJ2n7Wh z1qB@q6%`E~2jemL!Fh^}hYfz85|I!R5RwovP?C~TGO*Io(y{V!aPUb>O6%!)!~Op% zc=!h3pup!KRwBSr0q{6*2sm&L-2e})oA3y5u+IKNa7f6Ak5CX$;b9M9ul{`jn)3(= z0TCG<li6i8=o)3kSrx^3DjJi7W8(8t_%PJ~8lVjC z2VTPD&_&_>060+qq1c&?u#iAbP9wbT2jg5_aX>LlOOXw|dQJ8p&2XYYDc|J+YUT?3|Fxm{f?d*1vFWPGwXt8P3T#_TQB*NSP3+0+ndOe%v- zTZotCfofsS06&ki{<`Cj8{s5jFZc&1dl<{IBW%#V_!JjOm6+#&aRi;8ODL(?0fENIOtiNXjMhdO24CeDB#rNcC*<=TwpueFfx=2=r z-lt`qW^;vEFji%7kO25#YkwjKyZ93WFbbY!Q6-@Jz!9kqj>xgp2VhEYyMJwMYyHZV zG;7!MV>54LS*F?==$6(Z9S zfrEy``J-iu6G?#+q=$58MlrE}+C~G-hEMn#CuNuuVV;8#FHuD_feqmtfw~Ran|V#C zy+f^&q>|d(X{ubCVWs3Ai;Fz>-kAk`yX{^Qj_xV#NEV8oxtfCsq3%uYN0U4+Kcu%j z?Rzr+fnu%QVSgx7Z8;iqDfklVK3tl(C|B5~_ywyQf&|IJgyoV|q( z<1`6^2G=2%pTX$m#~!Q-7f>sA;n6 zsy{fJ>o;yxpRCMtZFb#E)dl;n&K%g;H?#HaC_HvnHuqN*d+9vB7ZNpfqqTsk*(((>8<~)=+HX!*Ss3~|# zShAf@XL@`g)$G$rAA9cU; zk+0v$7Rl=PDs_rN&*@^DQ<3}LIqeDu_8cvBZoZQK#xaB*@qDhG^d_fYSBG@Y_wC5B zy{FTF=4jI`H0PRGXlulcwJ$*KBs^);$y@AfTWB!przp%+gn+%ZU2qD$Eml|2m?K;y zsAx49(J!Aq5lqX4u5Rlh{1hD6V?uI0-0}%=eSBZT$;aWCJrM*G=&(~P~7QxUJFlHF+63{SfFhWU%gt&D(4Z~X54CH?JsJEHzO9{;5# z5f-P_*$Y>=CXYL(i4Vw1)$Y&DwihU}jeLyuS2hQ>zS%^7!rET)y)?ZI;W^c(neZ5; zcYHr@l=i48ImXZ(y)o<7>Av^Nw!8t!KDn{67gef*G5f-&iZ;`G@ej`@uBTkn0_QVc zw|RGr%!y|LdrjWk$H6iyi9+o%)D%pY)DHt@e}~ z-ryeSdskl$jkA%Gje(z=CvGUb4lqb$@>K02q8; zBpGv48m)G3Jz8nD`*7z;ch+s~JId9q{~KmJV4qG#VyhtwGh1U7ZW~XgF&CHVcfjI@4|IAMzt7B{D4ttmRhW76WO-cP6HX>7cPSIon_Pic=YB^cwH;qqm2b=+@OjfH55;lLt@>%R&7MejNBW98rLJXZZQtF zmm<7wrV(U^X%O}rZp($;Nb;(nTO##-Fk_K%y2c4)Yt?EsKDLVz&SyIxmRvPYUf)~A zkMkfE4X%Dz8*f>*I$-5J)wLSdUUaV&xP%U!WXidR7*F!E3|fu1supvKyq>T*84`M& z=Dt)zp4h*&a^3bbAWSy|{$~mRt znU?J9X@W)z1+)2SKH;RDEk{C{F~PxzePOC4k2I22=OxAKZEhYTo#jZLnzJRvL-#I` z%_%U{YhbA5LxSuc7mb|<#t0l8BZHy-cvj?r(|M5YOMU0wJ}PLj6z+91PP@u~sUN(0 zoPkUiqj+}m^;#5WI-p1sl3!d`><`0$1U4*Tus{#@{oJ~C_^ll&fIY{RWHLB)Iw~-5 z_trhoc*;Xx|5u&|7Q=~%>SU9dJXt>XnSP z$}G4aR=bB#EC~i5U_z8$Olb|B1Ec2J6a`$P64P%*8UxnscnAmYxki;vGRSH!M<=El z7AwT}?l;S3Ju)fk9NDaW<~K*9J6DCaimLP@Zry38*StONeVaYg4GMSV1sb;$0#63E znXJh6$=|17p)3iget{zQI-ZcSA4kztpbVusXh9 z97)P(^GVx?9}T_w+?VG}Hu2dxs!PdI;c!Skm{8crbnUpgGsmO6Y~0f~`3af#=;}JO zs+>jl(}Ww@TF9nIIp*io9|Ar+SXKeoJ2p0xqq^dDIUaz_3UMRe!*?g>RKH02EKY^8E=Ov%mKqCKc_O8|58B$F z2nPy$8uP`nq5-GE>)_IseB*$*+;W_EcowmS_|Q%w=6aW(&AB z%OtxG-1&Xrq>E%{bjzK4kBw z>Fssz$u`@4(H4(yPd(wlj>oT~6v>IV?P zZDj-meBV3Xh&lOz7Q@p@Wg;VMtEtz0tWmBTlY%+n#pR{sF{)xA5u*BuDd zu~BvH^44yI-2poCTSulFIMHH|6$HIN2!U|l513rs>o5b7&T060H4stH!Rj6uhJ>*c z|EXULN z@Ms{ehhc57nJbz5tP(eS6gqwNx4;1P!wL~Xzd!0hhz^)}wUrh90P!E%NrcHnd5moayrW^mwAO&F9eVphr}#sl@u5#&@cZG3Pef_5ki2d4No`s`w>3E)~NzQq~(%!wQ~iX zS=!>QgW*;6d%-30eCYi-s{}L5+4xRvjRMVc-|_!cJZOOW|D`V>G$9BAul9zT%D`1W z9M}_f^IBfCT+$nV07$(ZMgM6Q>awY7HarX62K->7rWiZ>Plf%@Tc$X)SUE~YSzKHO zOo@t904vq~)2~8z9N~Y(5ghjQaweijSq9}$13ISo#S19Gyn+S8<}IqydMB*M2Fv(F;m*Z^NjCKA@hf(byh~F_Wz8Y|LB9G zj>CREj|u0+^+~|!q^Z4wYAm~DH8vU0K5hJLx;^WW) zn1WdmfwUxh0&F)Ge zJJ$CZ;Gif2pJe@g3jR{7X$9eG;iwp*gh^4;#?q$usU`sYWi;VGk9zUsuxLCqS?i4> zU*!nKB+RzHh&TF;OaYU1boXkFHseTZ9^7*ClUf6WeOAm2`Zgc?XVxs@; z3fyjS*rbEGB3x27NK$sQDLqTsoYX+=I47hKrjQhxw>;|F(o#M)1Zs3=vHf+{4*=lU zQU(~L2n)P!C zOzn-%j;-zdo*A78MJ(b}aNl*Pd%bH4<%$K3cP@a%?zXvnXr7tnRf8PyxM=h2%x6XV zGm+MfF#t#t=FVq6y^o&};nl4gZ1=OgS0W6oT4??aAn_EswVeD=G?0*F3Ky5X?YMg! z*>m;`U68Bw-j3*NS)Xv59AyM$#IrAaBLy!3%T~RztCkOyD`0Oh)~c45m`f(fWkn+8 zFDQ?ehB?iesKfXr>kR(d+^nK;|$bJ0BgK9l#= zSZkY0hNH`T%pTpu&S<)sN$BmKep32<*GjviX5<~dm2S)BRn}Za<=11?iR0CbzUy=Y zs!S!r=YBKN!Hvrz2HB~apVp)gQ@jZ_C@MZHwF>*RQt`RvqEl`)rFXy;*9O;aJ^+IS zAuxBFkwxDhrD+zs6}YE;!WWE7N;x=xxy(hv8tOrT%;~evWtP_;i-tw#{=|s|_1gD} z+$ZPC>;C15y?f=k!B)}XV?@W+W5Jl7E#au2n|eXFYo52!7iV_nr>%rHTLnmp5t__ zeQ~n3Y!)Mwq>pgU`A+DOtI(5{uM`!T&#y7{XqPhrZyx}q50{b`55VTpH9@&go43WC zqZc?IJ_ikEfm4 zqiap;*teY3XjF&M`E)w#v0j2fK8>&^=3ARl7X5?sL7($cGUyT(&GjZ}T7K}UWUq6o zgZIm=(`C|a=eg_1ZeQ8aAv^V`3$rbeo%f|J-#teM&do=aJ4+|bCGzXl53;$~hV*A0ZA5ycpm&br> z1s-woGI3ag*H2HL@1`7`+#zk!nQo^`L}FmXBF9_OVvslb3Qd{^lg7NlT6j-eh)ldq zIsckeM z_udDHz~0vrwpZ3KkTG;-vI!dRfSCp$d>Y)?cj8N5Tr%KDYlI~&_w+W~Esn4I>jEK8 zFVT=y$0H**Z{;PZsC?US7QBb(=tZKtCHDjvqV8L^j>>H?^4A4kTvR^*B7Ecb4?qFk z;I3A-%I#4)i|WCd)!jLZw1itTxsZ$F`MsNa(gzoB&z!Z262^le=~~4I&U`Eb`C+z^ z-VqlxQ;MGC=e90n>dE>aoHV5TkqviF0s?l+z${VoH%t8KFvbH=8^6e$^AlVGU~39o z`MtfitBvEM13&NqqE=`^fHwS_HEw#UDbHmBR+1A|sO+c44k$ zHR9{S!q-(m1a+=}nRGQkrWg-S#Cg;_7%!4Ry2VnE5r>E(^0Gl4^r-P`1z2qO@^9(pRjEp!;DAe7B)FZP$pa4?IWYcn*v>YZ(G2ETw zy|C4)s}8H`Ddud6ogaW9O%*z&O_X=V^6P+mS%uG2EcbTZmk$RT3*(0o4D%(Ts3kn3 zR^3eYF*}KjX-S8m()tqnj4;!Sp!Ho z(7&2M@h1HM;%Et+(u{~Toh0sg@7K`vuJ8O(-mWug9HRvjKP2RmGqWQF%DK(bM_*a0 z>f3#KhBt~#=bL&FWEC}JiXdh?Q9fn5e)7$+{?1Bdf8>;*vDW!BMGjU0?$JBadm(AQ zHAmi$WF|HJ@r5-F$f^VPE+X>suAfbT1DUvi%}6k2#y?ZFyltx!?p zAr?D|oG4gh_c+U9sb>u3LP&?IzmiCo$x4%SP!Q8Q(jEtG(-GPNIhRV_K5L z7Q77k6Jdl2*V9zOs=X@?=vUZ(27Ngc&%L;RjmxGl273=|7++0XC*K z9Zp<^Y~Pm)w3D*jwEo<^OkS4Y<#>lqUb=O)W%Fa5t!Yi<%z$TRIO#_Z7Q3QZ2H5BD@(x_63h;Y($5taTf_%0;ZvK_v)P3}%^YaRF4ri60UEoVB z9tvN{)Jtntfs9Z(yp!blwx06#5$P9W8ouO?r4Ila4@;@S!F4qL>h!`rvxwm8$-&c` zq^<(9nR=GK@B4e0qjX45ZoSs3?|jeZ@13@KMK0R)%1IlSsLp0DH)BFK20FoEM2kwW zSasI{O!BwCJ+a#u@A3ot$06uqU?n&`1G^@J*u|t@Fqwmwe+Wf0fpg%{_PCq6A2+)j z2hE=ehK9p~efCY}}Fj~mMr1Qr~qOdueZ6a_2SDwHZ*lG#r|D%`UFa~RYpuWgUN;*|PxsXBBeqTj`RJnU2 z9PE7zrU|}#_j#k%TQeT63k<&b?|z^RNGOSfltB4MjA|mxqLrdoZ?;jS1BSRxcR{3 z&%l5U(~v7ESy(7pNhyb$1x}p^+*ny$*~6KoZMdfentT6QH1Dr`Dd@U^^%MTqyRNen zJ1b!yKUiiizxRn-n~&g}YvqM*{G%USoM1&>P*AuSldPnqET|FpU!M=af1wNq_3z-J zu56ng_&fk$SpR2Tg&VxTY(oJPP3gAh>wSjZ5#J1#nHbkU`Cof;dA1dQz?$+;E7aQf zK?$L1IL6d(9>vPMi+iISD+SJz*W!e)X$i&Pwc(XN-;gZPke+O!zgm29u4?v!xUP9C zcK48Y@K`NN;M7x{1@te z=@S`oF&M(3^!G8wji3Z4u|IZUp?p~QVc?q&l}!U>SAWC+@B3Q=M8Gx8SMIb+e*r+q z{Yg@g$}_Sz-mgRV1*RA!0Rj$rc-W8!5u7m!h@?;r;RvN(6Nx9m1}wb6UV=69pH!1u4ND1C3^0#GV9Vk5v%jLF1iBkM+~_oe#(k6e04;|1 zqVxcTK}B~<8@cW$rb+NWw4LZ7KVGkN-UHS;bD^cK+2-3`Rj^V98<9f`kPTuKt;S`5 z?|)V)15P$Dy~TG^p+BRJpbTIN2fb57!5|jT#s_X^pnNi>exLT+xuR}kI zLTF>DrKH5As1d;xUMq}JD`rE#xm<3PV^bKt~*|K(@>_s$+l6?PG9c;I$Y$I9Wx zA;xF_MZf_#OaTl`qJ^-80rMXYZnX;yHMnC5N`v2j=zq5Pz&RPG92*Z}aj95Z+R(pq z5>Xr9FJ8qsGy#`dMOy$X4%|!w<&^&whNI5zri}lV6#?4!$Ljbv_f0<2-3Nu?974eOh|NodBrc6s{g264H^#+vv zkI(-F!??JN@B<(iW`KcV-0ngu+-@)j;0A>UFo`kAQKI6|7gl5B1rI>b2tj!?@U%?! zpFY4#g}oL@l|*Hrm#l)1qwa_0RO)Vc;oKlpABihvuq26}r$$LgB-%uwqRxuRrpyG- z63Ji#aENg52nfiiNRQwVk-^yt-aSGBkWsL4aPbK7DcQKVMb!z2h+ndEs=YI%qUPWc zQ>IZ-)zB2Te@6Q%>$!xa)SLHy;OQb1@YE3;2Jiq}T8Nyd)7_1XLd)Qqf~l-gf<mu~bv_xL2)jRuX@t1;#}dEe+$KYBs8Ozc8vKSmQMe zW+znS+=sB{$!eWdtEK&;U{CqQ65Mz$g8{KO3091K?+PmZnxe)Uj z+Qa!s1zBptH)^y=Y^r;+YwUV(!nv}S<^CwP->`OJJ9$f5gUG$;btdeT%D1lTQVA%c1zi!li^! zRC4P;e}Vde23*`#o$}dkJ+39wA!C@gdHJNz_ROozn%~qZ35{gxr zfiN+FJmv8BeiZfN4}PZY+~4(EHI@`4GB%VeN^dL-nxv{!>bS=G=d1&YuW4g(RYo?9 z1bQp@-L75k9jgsahz$6&S+Al>N$6|(Uspyh?G^CV(>yb-uEMv?{QHK7y|JZHbV$py z%-C#HQ^wHzF5_m4mG%K(t4T}wM0ZA{r9PYV^B7{;x3r!Xhwb>CR?<2{=4)iW>-lFp zYAZW-ff6Srzcmf>ey26kFp~2&CwAle919+v=b#GbfQ_k(^GDH^U5h6Ij_hJl+$cY7 z`$l|J9)NY0%G=H3-AiTp4`ibZCebLFOx0X*^9LW5S-jM98V1l7TC$z>H_cy3Z}AyT z7cVLl@}RT$dt1%R4$rYgTUqZJB_<@D5gGBnLzk|&Ap3rHOWJjl)n=4BT|4ZgqT{Y# zt8otJt6vZPNdUZ->2VQc|t#}@1f$zuiGu7Z`2Eq_iUO7kLfvf z3+3l;rJH=!P82eCED=AEqW3F^^w0nBW|fbIo$+A)nzK!N%82P?SXGa`4vSNK00<2u zG?U_{jq8ikbd8p@c-wd;R3TJ+v(c9o9< z15te~^)#o6%yp?zaR-=9=hVgU2)|jpPHt`JGmCnIB+qepbmFikm>#nfBmU{7vA8^z zhTK~#rjjnUOtV*azuR=2pq%=qDo}!HCW$#qTWyAliZ8Xa(cAZ0uV^tvuLjr-#E|<6 zgACc9`oD!F+lpA=rLNEf$nCx{x6Vg$hB|ia>mt1(@zkT4(zdKQrNiynVbyP`+<(GC zZSyg_F+eKZ$i9krPDP!?9!-GQV7-#k7*{YGhxdf%D@)yd=P%=c?r60bP2qytty%-G zh7;7A?%TTQIkk;cPgbW*m6aq{m1>`^R}`Bmi$Y$X?QaEJ3_Auk*q^L1i~N3dGM6CL zP<_JeZDBHK(^_7!@i}$(_U*t}@%hy|H{~Q{;gP|bU)fn%xGdctI%`>elX|Q^@vKaK z!d+`Jp@j=)v%^wXH{7|-__X;}-BP#uIY3=_0IGNc zu~4o%m8|B~5EtZ$^}=3sv!lGEYU+H?Y3%_wM6P8#*6#HJvT!3ul#<{n9ja- zRGu5okTwJ1Zmk}BqcGi4_;~IURanbdr+P5iXG<{exUhhs+*pLQ^{jA#EZ#>o0{+2Mh|5& za#ugek0I`(zQL#5eLDARVY*Xa(DwdUqkel}vhN3?;f0iO-H(xqufvN&!zQI78i>uE z8>&m)ewHaoGgtXPku_dEb6PORWr~;1cC<+G5K=KBl%`A&gp6C>lB)v5Ri$FsN;P4>0AbJz7kC<~Dg6Mg7fXVHmZhEHpA*eA&u za?3ON*{!W8PYLPoTR+cR&PxuH$lp`AWkTjWWz)Zkn3TIiCEofih+Lm=9GE(9)!Yfc zt(H1<`s=^*222e=?7hC0lh4e7B}PtVI_{cAdxGNtdfZX}Ca>Ti9YS^NB6cCtzFtR} zgaj!>#THZKLuuFqeb58ou+VPMIV94Az9}?pq(nm5%Nr@`CDh7dQqUo_(1Ka~Jk;oawETtB8>b`mRyBtgh zO#hV*Tx!lPBM`YD{&wUnqnt2DkRmgRC{h$?KYyR zNy|HI%;HhKQrs~er!LN>c2+qWT)k%E+~E5H9eFKV;EhkieNbfqMTavz)YO`;;q)r^ zRKcAY}gLEwaGA zNB*t;%C<*Y+tgCdcJX-=MUjGgyz~ESiO9#&b61{-h<+|2 zO;mjRZ}0|pCLmN$E}rD#(9h}~)QpVO*=OQA z#Y%e{>N&D?0uC{dY5L(<8J1$SoXTWsj~6x5e9=~^#nEWa^lWqnid)H7wg`B&H>nuf zicIgRBoFD2ii?SfJ43AUH&TVFO^DDYcT;;?zvOP%hwr9IDk(8n^Rrc$KG_W$S^CCU zJn=ZugG;lxxPrOnJdw}Typ5n~t5&$I{si5!MLacZa-r_WCh{j~l7-Op=$9TV5idhN zglm&=R)0UNEvq|kz+%&#x}Q{2@c3ZLBldp!yX7N~c^eZPht|o%1isQe*+RisbVF_% zc)4$!;>pF);4JrP4@@UX#!&8hI;B{0l7;+j>*r10Q|es&1NFKQ)-tV2$Om$A@O-## zCLqC6viD-87K8StG^Ws5ct0&olMkYox>$?+Dv3O{NlG}G;g5QSmf4?q;BsuQo`^U|{x}>ACKXRkdd^tU`U+|LS znWy0^S2)LcB@0!EdDt(Vij$36^78r3tM}C?KI}e^X9-D}*M!iFT%zNr0Gf&Ck7!`A>(uLE(OdeRwb4qX3EiMVz=vWC3?2PE%-wA%a1ap0C zl~rRJyzSkY8Ag$Lm-Lq^*t1^}+zs%@8si;z!Aaw5c$|~Vez}RpL6m1>KPeiGJ-kE2 zbc5&X&fJgVtRw*RtiMc#4#s3H)KgHzHqg{R3E#R(bk3b8<&|L5d#($dxdtH$sL)Ko zW+BbDfPQKTs#e36Joca~N!pf`_Le7~Lv03)(7sml@e{h^6)?B<b% z4<^3n;sOFVdZ|+>M(^LPJA^2T?>N`FCB!o7f5xo^osCpJG~aJR*pRaJ`|hF>b2{X( z4aKEJ#QV2I?XR1|0J3}|ZH&ySn!Nm=`P+m<#hI$;xz?{pkF56P+%fUR#QbB?5vU@D z`>PliKDIXEyl0$1ZZC5zk$jU4dGg+)S}VQJ{2eA&|CmIoN#1+}`@$?!Mu3F2+9T02 ze0p5ot83?2=!y%bJ6DW(u9o4&WO$pZ4(odr6?FoB7XL4e)f!oeU;7hCto!x9u^3y2 z_p)OlA3aa{6K=F7$1_8Kool5Rz84;b!W+-X$m#2JgTdGR`~%<5^BB{h$tmHspv zRGNoo-aTFhEpL1CiLM*gJ|XE30ntfqZ6RW8RmFz7r7ZSdo2F`+dbIqX^P95F?^XML zEd;Je?~!LW2b^bUTSOUq6$IdZfuOEh#~DDY>}8&v?k$U}JNqeWBw+k5RaOv)s}jE= zQ}Q=>D-=P$ONyT$s*Ds6LSFrpWZV z9vm@*jijy=tPX3=aU<`d%SuI}+t_(ucyRkiyAE)B^U$L7DbCd`ZfC1GSJ8C#vU2#vSFtvhw(~TDanF;rn!a zWgH2WF*ekmAnI0Qm{vS{Le0(+uM5o()7|2IRkMwT_#?fPo-fNKuG}%_?WB5XSGAlb zor5}ub|f^JD<-m8x~AHfvW<5`F`lhl67hM38YaG)q~vy{D&^Yntrm?>4z^ZOsgY#Q z1rH+LbV>KeLE_&Mx4guoLMo);;h{zA@6Vg{<*=;A?ow0;2nhIdN=lYmb%EU~F+?HH zLaoso&FKfglw9l+vgl0wD}L>5CraD=W3%oYoYELRdWj9p+A0?Z!6LgiDg#Eu>Ssf0 z&g1y!IZG_R=3hb@lHbRp(1j)&W)S7%^q<5B2`lgE5Sih9hn&%pLfAg~&g4O!dAzEw zr6}!RX6}Ey-TL;=D!pNqHJX2g5o#)RC9PgCs$st=+TNbHeB0ziMr46BDXhn3@+9lb zakzM5tAy8y(qP%tE{ZSGapnb4Z^LN!*_y7=s>e||+mVpl^pnes7OO}vC4KH*VY&(u zBMQ9fD2JG^z22EVkkJ~(SO;UACk7d9{ug7_|C8~{@mt)aT#ZU+DQOUbF#6axF}^Fd zmhtBwd{#Y3lNT?|FIsK&gZ~-#n-Y__6Paff`W5$GI_?&4)>Y6wNn%X>=Sz?np7Qyo zZH9g7Vq#S+Wke2_L1>5intVG>$_RV=;j_%`e4O#OwWIFnFw^vf``;Nw$R9Y&G7L@Q zEpjyn?t&uTR?$ToG6e_w*elUbNC~oP3@8{6T6R7*{BS$ppthlyGy84Q%jeFbF-1n> zO)SGM6LD+T;r0urWn8w~gEyVb*0_W98_BXWEHC7aW9+`WLmR`7N+r~9=L(~xq$Jgb zc0`M~DlkIF1Q$x214|&HJK67p$TCg(T6J$4SH->xR%+&~^((0Nxq2lp^|OY^7-4i; zBL#gyG5+ECIpe3%Ik#hK5FP>?%G+Pa7_Z}b`G(asWH1;##`0)}=0g~DiAQ%12Cj5i z28T%p_C$R@L_1|{@r`H-3@utWDI40LfR4i!SA32m0qYI@45{@x~z)w#KlJvgXw}%|m zRo=DGsu9QXI-g+Tl7VIjr}mX;4fZ(YL6iQz z`lznb+}yW8^|YL;n26~KwXN#Dv2^Jf8J;RGE5MC0?77MSdMq!OZES zr@rC*vXhutbr*g#pI;TJ7-h(_N3>Ax$cW*Hvendxf#T2KHpKfFv0s*GVYIHa#ER76 zH)fn1{!z7-v31;4FFC;np`(vIh~mi%Kk6K0qRrbY_10$&xciNpno*F#wFH=MCWkdaFgK=U$FHh6#XJ6e393;9h_D1Zj72KeX!pg_>9E<8*a-g z^}Kf2k*_7=T(WO~W~`LQ`#b^ur_5KjDOs!UUZE)a4ErIxiW)A?ryWE_hQ{K-z66() zy-hd_Wf6g>qeoGlrK;PChpG^jPZRHd1~2MDVv*}eCafA~rLyFEm7f|EuG-#T2SgA< zQulXvo;0LIo^229Q9ItQ+RBrWH?~QpcDh9k(_=n;aXhtJh!9kR$kCNj9kJ=~BEU51 ziIB~(jdq=S3*TzWE4mQ!!I|ecuJydbjIPp*Xw5Ghu@wSqzc$S6Ix+3baF**T>Mt41 zK!k+2I%~h$4?s4Ot~MGVS3+Ob?$pC%AG>el2v|PfPf#)JsHx(Ctgl_0O>zUrPSn=nDj;t;8OUo=NMf=eZW`H&)xh@0RbL zug`wD9%>dDMf!g1Mmbzz7-EO^Yys;ref6{S7=chPEbgzvK3Ygwd;HLVo?}5(#ACVb zWsLd8mLOML?j@oEu`Ybe-Ndygs{ANWu zTYi}_YQ<948Jzmju!q^KwWli0(I_g&4zh3T`JS8oyS-JxRIlxlOkv13y^u$ebFvDyZKo49C5A{;Tr}MGMfceW3vqv{k;$^5ymBa8D>MecFsutjT zA|2ncpoEfZ3}EUt@Ng34X@75@l=LMd z^xZ7gESH4|2|k980z_jCp=#YZA)wxX8X~1diHoFqFvh?^Q;)oZcQ^W-l}yf5-ITM^aKZ zdfcjKlYl-&+8kEemP6lOR$P)7OO`b%yP(T25cq|hroP0p;{1@NydW2?&Uu!(^E(fD z#^%)iOUjTB^}P|c>sOo(_ivgq!yorSoV_H}q{tDvSL(K+bRbh52yrU?;o;#a1$BI; zG0RiGi1qO#MDdZ{{&bK@3)dmD(0ps&@XAgmQ$@l-h4Gx@t|NQC$u0q^d(ku>t~*n- zd~721PFdAKA^EX@ux5Tar!^~Q?kN4Q#)8B>%mcd&9luSEH|o>s^4tryTublkdEEI{ zKR#&=Y~)FcH*t4`M?g&TY~~}M>#}&vt3FYW)XMt2n{6+LCM@Vc2}fP)OONUg_(3`R zRab{`pOc0H4Vwb&4_9$Hs=7gmE~%pp$%I+QRt~Z=N*)eeji{_PhDB=gEL1PPqQmXj ziAC29F0k*5&JI!cBe@oy3-j>BSk^9W)qi|x9siuq!?B_AiaL9Ia3GgP?P`@aa0sC%Vx~ z4_H;|sIZ_baSi_@V?ArUq-+ig)fyk1eXqmTJP^R3h2&8I=PKcQB=1Si$Yi>2^`ec` zWhT-zHa%mNK+fB?4Hfg(dl$9ssVh57orM0LPj=M|2|5Z33$ZS1MD#ToTy?*a5E<)o zZ^vgVRHt{{s?S|cu9e|pBs<_KW^^?c+z zVk*-fa)Av4H$i8mAsYz;V>N#~@y4qSwKG%ox#ZW_-xaK$Fo)u_7H+~xDQI%!Bh|re zEIa^~TT?%8*jT^u!yxl1>%qYTu)I_Iwf#Cm!)=kQd!PDS6W_)FgT0q+ohn_P|7b-8%kc;m zg1^9mPpG^{HSkKoxNcleZ|3O*V?9Y(hvnWYam7N)*3PotcW%Kd$xrtzn4cx+@DGp{ zFPwjuW6B=Zy)W%}`8}SIrnZJ4SEixC`5nMMSLxD`jCML$)Oa|F+)t9}6J=&fRyZ_^ z*(>evV$1-$K&$Aa2X9j!@6ZDeqAYa1l-8b9FTg}aF(uUeG0nO9eI}>KD(22{Y3iez z8sj(PllCVvngk!res$*`DI4Nz8|c28;b3g=9C+P-zJQd-I3R2Rjn*zpn2l7K`Dk-4 zq4GHFR>DRKlZC)XE(X!Rv+KEpkgX@Ph)0`3j~T?RfLQbFSRt^V`+L0ShrurdA)6#R zbvLEIWqYfi#>&qP=f_x+*)14zkd8ci08%!rf(xnWtQ7*>#*Q3lqkb5ZF8F>;{gl*e(oha^!C7JqB6_d~123dt*fdvJq(?6p*0LOR6U zl~o@(cjQPyT3~|OL^gOFW$f2uVn7?jn#?#D74*G0zSOzzEpH3+v@4X!>%a#ZdTNAo z02SDS+U^x)AN~i#!qbx+7~#+diA%C-494h3`5HW7V|SpXT!d-y6K;E6??0eZ_5aM0iGa7jgD1?z-2)tt(?%)HrV0P2IbUwxg)d%!3 z4(Qq8t4L!w^x)eVTb&7NdkTc^eWb9hI4uNo=4Vx(!X0`ZmUUTkqhL%zXoLtLh)Z5V zt{c8kL1$SYHBbFM)7D;w($|K!o|>Tg+asAc(_eT~?!65~_r`GLc;t~??0R+=C$8+% zSU9dXJbLgR#?h~h;~9v{d|1ty%Q<2)Xi_iT>Z%Bt?C^@A1-{?xP6+qny4pNWax8sr zh$_z;Rh0)xfA?_O?hY?gv-D6ddJNR4@Y&jc|MeC)wpLV5P2%7;{EV$#ZcqAzo!qmx z?ntfHdsSvdZRqSGv5P*ec0FDX*}Bmbt}B=gb58YCcP~YrMboq0D&KRi(a*1$I=D`) z(2;{aX$+9#~ce9s7Dc;AlEy)1ge>u4P`ls#tV!AH}{Mrf3Ev0g>k_on;O1VUFJ zja5^PD~MNp_xa--s%kd#tw&d-JDVyx?UVu)d+29O8LvL)y+8u|%P4{5!jguGKBVVX zp!?(Q-W+--0V4ud;Ga3@%BC&Ar4xVyW%TLQs?ySqbxoXLB9 zegDO|`1jpj(`&Du>guZMs^_U@SzO2wiCx{s6}xlc&#oh~?+TXf7P=r0OSNAfr7?9= z+=L&!eF>@TAe>!T(a=TM0@E)Zl#UnR35M&^|&$%M!ToyO7X*>OO8DdjGdIhHXPX z?svWHw5|YD^yy!Ed6saf6-1ZQANVTlA1J0y8BhWitD!fgc0O*ZogU?W{Bt5=|3G*4 z0jq4((3_~e7hRJuRM`){U|z**Fm`udnq^RoEE9-!$k5NS%TzM(uPX~_hfO9JTpe|K z%R@gT`}pR!(lNGD0G4yAhj zMEi$N{5aLE!7mDWy`(!%x!PN3{hv3%S)|U`OK02zn;mkigLW|8Cqk||nYC#RM3piP z1hL@Q<|b|GXjZHE1wYf7mwb8HTsHNp&aOo8IRTPw{J4rdTvT7LGO=6`h|uC8t^tE^ z2nXn^x%`~8UdLhe>F%x^KudaWuj^CIgH|`GNqTS1huhCeAzR|zcVN*+D^GZvg@t6{ zt%Jlv;t+k^cO{`*Oyu4vy&A6z3MJqkIX9c1AKljGEZooh3;N(+_BT<651L-I+e8z) zJj{Ug6s~`2z968B!3)qy`JqVw0XcMz?Z)C-ni;Puf&MR5s_EUj`9^N zc;)D0ekKK2F19`-g_u62@O@lqzi$?uQmFd1QaNobI;MW=A>yG|U2xA+(&{n4;JspG zJ-vAO_MWK+!A_SoceK(e*pjJyX<)UFz?T`Y9-H}d$jADsFSt4t`-_TXMgbZ8=s-uI zN}uEaz=#(l8|*5;4k$FC@p&!SWuo}TbavOrfL;Xic}AxxdwTfr^OtTM9$#(&gBgL1 zCgRm~-OP9kaZ(%GS-8HpsZuFAHf+g8Ui_asA_>2N z{}WoY+y{;)wte$I9;{JE2LYtY*L*^DeR{mjQxi_YwYJXSbXjlVYbWV!4!n?iElyk& zy^M>mx?ICf@W0anrFqwS(ZZjxm2p{Ct18%;%=`5whuQRB?n4Dp#-@jXfH)`T4>T}@ z(>zL!clT~7L2ehKJ&TDg2W)5kvy+LcyuryarP5q}=lE*g1$Wvc=HHClGs`X=cHYVQ zV}5aV#pFaKx{*62j~+E^{o=!<`%)BcQ1;0AmTT>}S>h0q=-1Jorgo9}7wS1Vyu?Kz`8EX1p_-4{J;lNJ2x?N3deQ?__Q4X`u)~;kVttI`SSwqY})U zf!AS6{dh$TKArl?Vs+3KubJMLAtooil(z? zH&-|YJnm*^mH@3dxDfSU*-TRgaxN1LCP6qu6!CF@J3Oh0=h9*XU1M@+6Ladmu>#JL zivIKXm3}!-e;8OYA`>woR4Cl#xB3fxB-`Hfqdc^pNib+J^$P$`DP<2hsrEp}I zQ_(``<1Ijf%natpKc5HM-Rbhu=J%eJL$8^zKwH{4agt`@cU1m zpuThV^OMMoOu|w6wC==YEgygQfoIad0O`QgblvY9_mqR|jApUcdy(Lkr*{YU$F~Ua zvVw5Wf>5GNfOcC6tG6U_>qy0qoKn(JYXY~@{Ms4=6*zcF8aRn@6ME~GsrJ;*92N6^ zY&>yh34%;EV*Zw;eUAUiZ&wupmR#g{_0^$e6Jn*c<*U&c;U$E65sQ5)%m&SUYzMv% zL@{=a8s{6R;#~Aq!_0ZP+Tc)HXZ5ttQ41tW7Sc)-6RcWb|JVmk8IeRFVEm!eAw1hE z38h>Y8j7T!0u5>#PY-3{)X9)G95$Wv?EN>(`ptIATg601g<1x!fptG-rH!E8_D@^y z1dNbQ@fN$x9!1XHW+PoaRWA7IS^)5E@W13I|A?-6U)7!w%dBI^uO*pI%56K)#`Thv z-ykObUb-b&0wAUMakr6}NE zsL^B24*0tdMdL@1LP5fH`2~=$lzpVC69|=}~RgpfhWupn~ZWk?Y`?*YnkT_6$PAm99BukW^KI)qfJ>l z7gXMiPUofoC9Bro+CW7mC0xY!TbAfh0b1`nTbEap3tQFSf^P~N%gc}L-aK4q7FyV7 z-@5mo0)~jBS5zmee1R-;UOJh> z6|SRB=#IA`W&$$?_C^Vd&&Iv7(>d?yU;US>%S-BE#sGTl9D^{`XhF(sl)+s)nO|&? ze4$V+tST@VS}vAD#eC`K%Zkygf8sG>Pkk)Z^}zOVizMU#CQ8@4t$~e;W)dyD-enef^M{H?8TfvnQ52E(dj(=QWa6&O0Hv@R6& zpj@3*{UYB9a;QNv9v$&h2&FMY3{H@X_2m2D0qm|zED*}8veH-axyoutqwF+`s)m|j zar8t1hZeL@p<%kzlZ}vgS;u%!PwYlakwmV{6rHdH6q~lQx|_r;Y%Ugs)4647*q_6- zwwzIk*Nalst^J^^%Bw8uzG*yzsz3`;;iL@i*opd5c?gEWnV1H?)A63{rHAr_EeJa! zvLVTlcpd~f@!0}a1uC}NP)0oLH_psD)Bjj%z?;CVe~Ob-vUkv+@w|UkHrAF6MB^bW zXERG#+UDPn6}LdfiHN*L4Y63-QVWLf!d<@>3DgG5QHbSQ0JwNPO~03wt&=#W40a`s znR6ty-#LlsAr&j8WQN5p%Z(NJ26hwHL~*DZ#|M_0tKqlLJC0TPJ6p-04~_mvsh2yJ zcF|vIuCXa-`NLj43JP}KqP;}qDCMonly(h@e*0Mh66D5NoA6m#T_!NLI=5w|`!(Ki0SOZ$ zAkviwBa7y?yDKq$8j(Iryu&3z*5dMo_^O$^eVtYvG5y>wBjjSkU=jo>qer@qPsa{4_M z(Xibqwva-z)kVxKEJq4Xr}L8~Cea8ByVGjJxFPv1my_RMIXt})#m?ixGH;vQLnGs& z(%FW1e$SO?YtGfHiyh}F)3FgT*q%X`S4URO%=#xn@3tOVYJ8{~sR?|^irvM{_V*at zT}D$9Hho10>?JS#r@W#HExX0O;Wi%j-mV4;`RymI_fb#wWcsYLnJnWd4+R zQTCq409!kbtSIN$TtcWjf>tL_i%h(cneO6VujA%+V$YUuQNPitngyJsBYmT?m*Ew)fQL(Vb{TWhqd;;-aCMu8Jqy zw2Yd4`Iz-T{h?>b=3Q-OxR>m>!p8lX-+x@r`JYI8mIyx0sOg>cvh<4&)gh4hba2An zmR(mU>;-6VwQc7Xa@K?Gzs5RDL)+B7sH@|A+w)j!YwDZLn}&KJI*N59c#fg7>AE=i zINsqY>+;Z6qnqY*iv1VLEcom0AhDH{^4ovv?*(W=TKE((gi)J1#w**@D^sPqAJ0Z^ z$j~1H?&D{nlhjt!m+STEj0Qt@%!(D8{b_$=V*B5$ zHD`O^3SIt%ifHf~oz})(b3JpS2zs40H@I9~Uii*uhH}v@Y~*(dvxFpw zA+1~<>mw=oBLbi^HIV`mbpE*1zc|AKIGkV{vP6dakoiot8>A z4!wuo%14@qFmIw*7bgnXj!kmRyL%p#H&@EfeAD#S@6H6OJ&LhiV{HA!) zQ8Y`L$Bq9Tg)GEP$gy?S^oPqB1^qt zJMHL~Uk18aQ&>09jAbl$r2d*J!NI)XdVmo{RWDpYz_TPN^D#*p!zvS2^PUf-Z`G5nB9L zSnclzT+*fn7R5oMKo14@r@pE`I ze3}FQ5~U+Xv;woLD?&R1@SMdKn`3N0%}d>SwkoGzP}bmzboU+(ZNONteR?hP#JA9zYRE}5ryhmi9r+hJ}$VsJ66eF~hT_rk;{+D>g#GN`L(iD)H$%URv4H-v_z zS8NRLobH1LD(Vn>O8?W?juDIdbm`_;YC+B)1Uot(VJV@yVyEpYT*ztMXMPbjVW8}s zm5yBhVX3%jNNmB6FX15?X~x&$8R~&CKro?`7e;CJVecI@#=9J?J&k1Q^zj%F84qTP zbPUJI4atIQxEPyO2mpT|-1O;d9>CnVUAH11ws;v8$ccDV}ac2<q3&_&!wTy->U&lk5cVKJxb9R0Iig(AXDxJKGq4N#1xnY{BZl`vUHL;ndgi>@XYSTCgUxaNIFXF0C@0)X7TNicC_GjvQ ztr@xX9n#fJzpT7HS-e#ry?SurQZh;zH%PMWs>_Q+ei|7D16dA89Ot^8%zgP*V-v;V z=UU|U2G|-D8cN~^u(ut)Rh_yuZ}zoAT;cspnTQ{#fT*Eg*#53NQJgvbq0%VMGSDbB zpb12ox#9fUH9M8l()~6kFyoVTD4>7o((h*{n^hL83_%gyHLpBs2$HvORIcz zeCP>s?ytt!8_cs@Kg(fmNgZDKmHV0dwaV7N6|UkBG!>1)20n)#j(JYa%t$>0zji+} za(I*i?l~5PWHk;{KLKT^rnEG~8l^h^YHg=X0+8S;iFhD;M&s5W?zLD*NAI+~f6yf} zKsOhU;09vj)lK8lKuBOASqSsTD7D-#En9kwA@-+-bRERwB3TUftK_4_Gm?`W+rJ!c z8V*JIk;*wSu&`-(aKZz7DE<=O?H%1}`%`rBr zj`aar@#AMRq6?B}^4GFhz(Rlf(G}q@E_-E(N2^4H4!m)stH`W-#k?bK%{74=H4{x? zB6Sf18yibRl+kUyIyX#xSlTo!%M^xGb_^_!6y?X^k$#TFQI(WqH{T2PZMF2=p?MaK z2f!Y}ERcH7vn^|tZDLR;0H-Q^tbyZ?G?7UlIkYr6KLrPnMT&w8A=at-$*^CUQv$la zp*9NVcNaT)Z4*HU@}|f)v~;r1TiNK{CzI(r&Ce|YW^v0?QWB=GA|{?GZx%-c9-R17 zFIQ(Ho+B8)3+Qc6%zd&1h6YkP-6YVeQyuPFU$C)p3rLVssmFk34c79jC=rG=fH_L} z^Y#K1?Mb0x)=!J||1f;^50rWdxXAD`3LnH{VPjo8ZIU;CtkU)`gRuK(SmaFPNsB?h0arwM+5SUmvL&Q%t z85E>Z5&~)b2YQ3}A8^Anl4O#Q@7JY9uv|(8MfPz@rOe0;uCAy?;gwAQjVi0yGES_p z?h;`bIU-*q3wf!=5{2HAS(DdEVOAT5ktuKFsN8)J)Y{zvD( zr(Est_{Q#>jx-F`7Sx_j`{92xv^}bPxiykDTFQ7~dhc4A)ww_DiR`WAxzl>{`o9N( z23n=16>qh~Uek0wAtr-93J#q}{)OT_uu%z*yL|am1DU7rKoo%Cg8&XS^;dh8k40{m zE=(7&Eip3z6LBvq!&2ENm480+ewx!>8(vQr6mXVD_?ehccU1DFeJ7Q2ad{f(;^Fkv z_~G?yb;CeO%B=tU3D!-NNs+Yg+aH!2&dZYQMC~r|yH+W)S$rG*8rtKGb#O3CEpl^1 zSh5~E6-$!GS;vmz1S#jKVxJn_e|1i^#X3hK|2)_+Kg3m46!vITR(~Ad3(8S4wzuY( zA;t(*RNzdUbA{*q60*myOKCfZ zSSAEwT-~zu*X>h2S~ZU{TrIutUC)Y4){tO$t$tCTRF~NRP*E=~Y~GJ|U90UU14#;S zGlsxY?~zzZ-Q~ECZxsCiarmZ3iQd5$o&UJZ{ze1gP*l`P|}5>3^b#oXr3*IAUlL2je^D^~`l@z_vZ0u{S%M$&)aS*Ij! z-hNtY`2m7T{0c%9|7%sFe=RsVD`#s|FqQD7t3d;di(Lj|YHU}Qc*d$<$J=VPXT>6B z3OU;=WJVhDIq*|VAFqnsn}13D!LHm&D&u8PG(5yyF{(^`e(D=p=Oq90U*n3qEJ&2G zpti}lu$a4dBmQsh1T1Hdtcc{D~%)d5FjW%D3q_w1^wDc{5;~1iM3c$bb ziJQs-Loo06jkNuWrh>(DsmpA1L12D+XMxS{ERq)f@ZtAINzybplW5i2;}=KW_=G3* z#>w(6BIiecp~@#>B+daN?Ao??)o#UGYVLxg&$*(b>wsS7=$Wd=@Z7&p@^8}U3e}2I z&g_oikS81WguVK^CTR-3(7l#(1>}LSVCd>55Y_z~W@bYElp0Mq%K~P51c>4+RYI}# zpHXYgig7oHso2kqR5CT>4Vog>TkDZ1;`D_O$+AiB30ftzWGbmUT>wr5G@@Rc3$vp% zwdPLsKfcn3JmVIMPKP(X+q4WaR%_kR*l_QkFEq(l06CN)lu03-g|Ut+8I`MPPiltK zUwhM@^z=`bUARfFT!x4ff^N_3hREaZ#Iedfq2eVISz$jaT$2!k3k*Sw^Pq(Ou-M_EdYrJSmwf?&JJNH!_h z-&nn%za86-q5g$ZFcdR-`E&#G7iw-Pp71@j%fI)|O_)H9>d{R@v1Bk4E3&^lL&z65 z`3F^p>MQ_bmEhhsR+N8LEp|bjUJVh#-Cctu^UNw-{z9>z=PvyT{0n6dp>%6tLBT-7 zKyHLUMngn^hlhsrkbr@O!iK}b!KDO>Nd?+E=P?XvLpD4QvuD;_jeuoU_ zdTp8HsN%CkkDWX31pK(5KTPPoK)qkZ`gd|CNDHIW1XVYb9qXU(_}v9vU!H=*47UB$ z*$cZhOzSf#glqL0HAK2;FZCmX%5-pt!mg?>kr_5M^hu1!>8{L`ol;qZV_Sc_sY|nNi*)U(D*Xv7rj{`V!YA62maFW)Vpu|rqFC}$p5&0|Kpp+-+8Wlgw7 zAQZzc&Ci8mdQQset|dG**wvXDu|ml7hKXO9efs42=9dusiH~G#^M#Gy=eC?4R@ov1 zJ4fKK+_7vJ^)Y9!;xZ1Q*AJQ^e%i3HQ>76`>C+u*zSGf7?4W9w6AiS z{*B=>e%(MRyo{x>>`#_6pxkvxuG8H92y^(dkWbd2AiqI5D9!~#X1t&74A4Q;@x!ag zp(~3(KLdM(*s1MVeb+jg%F1G^u=x|=$zPwK)g zuZVuc^RjBB{duk~!{6{nx4v0l@&8dulgc(YTL!P)2I^c*(#Sy)T}E_xO={>vLE9fo zDS4r6X);W{Vubd45iK6*n)ezQ{>a`P{wico?6@lm<1yl1o3|Ird6>Eiwa>$xDl8fA zjFw0y=?Jh2N4W_EjGemBg!I%smb8Z&vox@8d5*|s339AStKf9EMUadr{cmY}9+3(N zB&YiZ2dLxFALeEIWAE3eLmUBq0k!jVfbnGdUU*0dtk+NxCF>hZYhmMrhX35)&ki5< zRKD=;(}eFDD6zICwOjjo4(3+Z*o*>q=Yy{~=hZp+cPw}Xfbu`v?hL+OCj}}k3%CN^ za&G0;z4*D?xv86kMhJE3+F1A(Y@h56I#S7q>L}JoPw^k#(hfA^eKQp)8ctVr;tQX5n(wuC4>kK@S(aHHUirpOekHpjGJxdjR!jmLzfy*fo- z{YS#~|0H|~_wJGwD7lOeKu`C~?!x~wqfY|UO?@^=h36)OWMaxhtSi22FgnLc9Q@^A zd@C#cd(B!UK~Dqc&Nzx^p`@+1GFUDZtKdv-1(Cld;55%WQWuXVQu81wyEm8a`^$|r z?Ipi{w-@&=Mfk^jBH$!fn64N-@Z8Lik7PGy(9K+WT7BmMe-ehgUTh67LNl(+e8(86 z28`2V&HTG8o{C|uf(1dE(9#qNHaR2FS*?|Wr1p4xkn)3``BsuUh5?#^Ro5J!p)xv~ z64E&ugeoFvk8wDxv0+UE(YQFf|DkZ13t0&&sP%UT?*fV;+c`sJtj(WV4rR7S*OR!} ze4;W@_5(1%`E^C|MShYGaWHW$zgFPjV?ys|zw^u)|mp zzZW@8AK3(#)WH~G<;aq4UyCnJPZjD`|KPIx3zcGfApP~X&2xa+8MM(ojn(Popz(Qh z7LG&zWPViDV}{J>c)!JXK3RV9G|@|#S6)(M^44FdY@Zo?KI^^N>16@>h=gV5YxNKC zt%4U8djc{e>f-tJ=JpK#?4uW9#L)@1iZN!!>c`KH41fNk0y}{qA^&mO_5+Xn-sN;{16^U3|i^_$7(e>3CjR*S7Qh z-mmCR%`tAs|zS#Rkr16}7&uyK*XNwU$%GAwx$C8-|d_cgGnyx0WU(pT3CT!&mTp zWBoGJqLPYmBJ>c^8d`?a<_E??^-Ti@hT)~TYLICauV8jGC#<8)4ii}I{b#p$82XoN z%5mXx5|{dBy}@jMw$WV230l~>3h42FD;|c-XS_dbGEtfX$+wxY21XHsb5V68*q&geyI&{ zy*^xJUJ9U{Q$06$n$w_}=ecFqIxIwAw2+E_F(m=sH< zPMV=Un^53GazGVHYZQPz>+7va$>6C6!_XiuUQee(~nJ_cz!L9acq+1SWfk&Z+1iAR*D_6J*f1! zQPQ7tK(uHUane||)U8SSB$Dfl2s{4q4Hd=-x1B;G@JI4@f-V%60@uF_Q2$0>Qimm zs5YcBp${DH<$NXM=zy(r?kI7@oD~dpszm+>%BXCTSm$U3u4j)`1j1Ua9P_ms^?zzAxdspPHo>g%$ZYb`dF-ZNrrx^6Mt4KiV>?b0pL)nYE~_ zP$NYeGJGE%|B*; z360 z=oF>sY+arM$80X*tGzsw7EB*>n+4SniQp>A$lxp75~+-xSL~p^JiDx2V-V3xY@;$O z%NdIb#SY#8v#?`ld6Tg{OmAq?i@GwZP~S=LWiP-DO2 zfPQfik0+e)UhF2jS_}+b2F1xi5y*zbJ#vULGVD8G8!5#cpJ{*>FEGjEQ~`dQ zcOU0y^v1QfPn5adbKorrTEV`n1jZ+_CsbJ?7Kr{!{MaVr<5I+;lH8( zlWWm?@-3xS25%g{URt*s)5O45P+KHTQmBiS5l41G*l2XM69dicDjS8R&7MI?rhX$| z9OeEVX^1FAvg=?cGlm5GH&pt&yd*=Av8$S^(AY%ltYRug)@W2>D^WA(SW;|dj#Bb* zPY9}ZL!MjVzPnal92|C{3IUIgvC$FM07?EV&8XVOsA2{>=keTXV!WOswB5r0g)(sH`pxVp$E*LSx0bY$^ho1gZ(Ce+BX zgV-v@;O*LCgouh%LTJjh>6fNe1i)!k?_(K>@#hAJi=BY zGE;k|p=-ghx5_WRZ|zIf2wi`nNO=!AA^h@IFVd>=cc9tAO;Z$>jb7>?tb6ny`W{KE z@4c#}i7OkeEN~Kt%gx{BlP5$=yT6^}6F42x4XRhqN%6t?;^?rmV5dyeoKLqcsOHK2 zbb#$ru$;PP7F>-8@AY=H`&w$0QopRgaXn7;V8}$bm*lMCBkc85YEVhMoV!yFW|9fq zOOmzYH%4z?uXN91iF#K}mflTpD~cK^sdvEd|BV->>NLNJv8A%AlG31C6zsX}U(Y-$ zZwF~!_}FM_&U^rCK^~wXBnkagUjoVFg9|^`O?Sx!Zea>pf;c8<%({Q|nH^JacOn1z zeADz)ALFn#kY)z$^0QBF!@D0pPDEp@pW1(>)BE4M#(XVf)^jdx86Y`CCpVU>tB zuWv)APNSav7T`?DGY-4Nv|7{Snoz5!!&0eVGg@vN53J3Ee_3g#hG{28yjf!D{fT1E zpg%UfmE;4?O=&gw@ZDbf3Hai_OYc~H3~3&%p!09Y^Dod7$$qC>#(szjxJE8nhoW^b zyHTy4i$#2Ft$oO_M0HjPEsBbN7v4b>>76ZMU^64jzyQgDIvRU(8vw zWPJAM{3hPn^}8Sq7x3jCh>#A0#0LkcK;;6~LD|#%`NK@4|3rICT1gYuQz2?o{Y!3t{~rZg8TZEN4}C z0NFhS4PVz}Y>K%r9px4qj2)fe-bF0^YHjv9n(WTJK5}pczXS&VM!l-6Fb>;jtTbAc zK>wvDj2JFDuA*@Qh}BhoWY_h{4$zT9GX>R%Nz*M!2arbiK*p^`yCvbGMUsmhg)T~` zogo2NWbfPXr~}*^P`(nPi=GphNo*`lsV|mWNcALV zT9G=LCo(Lc$(c{p)vLpUgeC#3E!-5SI2<4q|L5aG>&KDQ6FuD;dD&Is2 zkhb{2IeyUMrXlL3Ba;z9Ch9BN|Oh{&lpP3T)V)to~umT2O}(UETHGV#M=KbH!v$e0++(+CsN zSl4jZIVZ1@nNopF65IvlxKhF>5$T-|oFbj-96=Jh9ctiE1@X35d7DPBaSD)+;H0*g6&q6ycF7_o7Ecw|X6Ib0dkC_CeD&2k z4?8=&aA-}O)<}TCveL}yP3kxGgUUoI;yiH&aiWuC5M_T*)_gbr}=-st| zZJZ9OO_)~7+%}NDF!kg;Xf>^I7$qw`T-gJy4AHH+g(f9~Yxw(2pl-SRg!wfr8=mMO zCV?;L;%ft?iQ)j@x|yb=-9tNF>u8~|kQNpK7`dl5y417E$Ynes8{9URCTU895-IJ5 zXfeN$gmepw!q10Mxeweej^snobY3zU8wjP`Z4wJ<@b@jSL5`$!bslp5J**O@Yq>%d z_0hQbLdi?M!t9H9mHsEW9WxV>jiGKMeQ!=g11Yf_90%3xV6v_G>rUWzaJ=|>#w6Gt z!7>DF1j_a~&rQ84Qn+njH9Y0@^rEgU;RTPsTLbVLq$5sDYi4iv7pfSYk zd_X9gsDx|AO^DW24B~@?;DVWf=pZLF6g$J!A2^X~-$QzCY`9=kG+Yy0qnw*_=_~EN zmvYy&A-eT751Sl#79(PY&mVc)jF^}V$sWk(4;x?qGTBP>v}D_%V|3P5Q`KS5v8b{c=sf7;8 zFqg%9AX3{CQ8=vcoli2JJISLN>1js61v%7CNzMThI}#;JFoE~YZVWlH2&RkFfePwL zBC^c9cfypX9rvfb?57aJ6EZ_D5mra$NvyCy!xp?Lb-5yfL}CO8w=pD8^(npBqbtWe z0xUCvv>QNXDu@&m73$6t98wT%g8dU~(ucaHlfk$P7=<%SWg&vjyO`+Hl9|^Z7$A zOeO(-ugx8&LSF<0ZU{UYi$(r=E)z>S{3BcrF%?<<@A04krSP9aY&X{NJ*GFAU~Q`F zNp2ioI&(wWsc32Nd<&ggwXsqM(GTlAYEbad$|0uUnUksjzg3*x5Yc&Xb8vjKnM?>! zeF#^==usY-oz_FiVY|77gsk8r|G95&P2beFjv@L;uh@|)xJzj4aebFyE>LydpS;AD7Kmxcxl$Oc>#b9|?L=2Rh2C6xE zG!vK>JSXB`qb3?siIObloPr!}Ofs{EC#G+aQ~>t#!QGX!-OA zf#wb~D}+LF_GHM{J#CA8gfsC=llm~MJPCZ*5_RI6@5?mIa_Wiw4B5Dv}6#;FrRVu8jR zQ|+?GOQ9jvK@6*Cv+GW&!C8o4Q56s=%jKop=|6|B&CB5mKC>W1A3vz>k1ILtRO+cr;txw^|Xo7o4;1vI6I zA&x~YuD~?WRJ`lK*kG?PX+sv)HOUaUsmtw& z{ctGOOL3U4rz&j>uVP`l3tM8SEILA*^pL?ZaA@R_k_V?32mH)j0@U@J+?Gx!(Wd^w zI{)2K(vy=Us;57#LIjbWB|e)O+E#;H%DNrEe{_@$K&(}{)-vmwp^>XD?2CyX6{Lhy za!(R2Q$+KF-6fUr?s({!w4@$2Dggwpg`!?@Us5R)ic z08>>Z7#koZArTNXuS$mrlK>S+4a8m-{t3dHnKQk{ovDKfN3}$BhGK7s_R6T|S7ZMR z#d>?Gs$3g5+|N0|MJDBs7#%NfIJ8Lr?{*!TV+aK(mQIFwGKUd}%}YnaYZcDHmUls; zS#KH5QZE}E@72DIWZ zPDrZtVaRC?ff+sIP+_6#|j?V(2=p@p+rvTQt+G`62yXR5@5@B(b$-7-lj3+#&Deo1XCzPC>y*N3}&uX0<*I5PeO-4)iJc@c~< zx)tZNom4Dw^Nm(2y^EI>Gu^J&4&|cOwGd=fnl$LGy!#_PD3YeTk~BID%?Yi2hm{%b z2i4A&VXyz|$~)|>Ep7~d{0=UXUY-KDajD~JQ-3~tbfC}oRS+rn^3#ZiGBl2>aXSy3 z=kE{c+u4kIqR2Y}4Sj#O;urUZsUhW=y&vVEt*0_`OwyDc*JT?t%Au`m4bn+-N)kSv zK91 {ReJKDzsq0S-SERkON=-c09|2#}%+_b0t3Ya`yJPygodggISBkbAcyLjE*Yb3t~UOjgkC_x9x z0%ciuS;!aTIaZoh3#Ky z{Mn*dN(JR&aE6UjX}(iKdiHtp)?Dn+DT-#nTL!|b0~qQwX}hrXNf8(CFUUz3Ck@ZO zJr(~a$g9DPz8~o<709L)cO9H&>>POetiuW*8k;I$=Ny)+Qs(gZi0C>6uk}eX-yo2u z_Q?nPbZb&5ZAQ%xm3P5`a##*2TCphkfJs_WqJZj*G(~2M8EXJEwmy^-`Ohh+P)o8d z32-I3#1_iA1go*xr0xoVszj#v7K+l0sS|8GX(C^BPqg!rz>xH+2_DDrF2nbthIsV< zH#H9BPA2g(B$J;T3)c(AivPyJfRi z+O=6D@RCc02uj|UQPXi!$ED@sxGcSV0|n% zESt|!TTYS4n&=IT7>A!CxHRwu+mfH3gAvO8qtFqES*XOFv7wd=(p#vB_9p|lJGH#< zpqSTvztq@Vj38pJ1E@?*IZalBhiY7qD8lr9he#B2TuHSjNRe7gSNXyK0PN+vgGpJs zkbLPNQfDEW2OTT{tZkrJ@nZ(^`bK0RxEf-n_Qzz3q-$Mdh=Fz>d(I~bjhXwkwAbE#ajxzb1>IY4l z^bvM+z;j4T3J$DIIy7VdwwZsMK|r*zVIa~_TNNHxo0tP0S2=I_2a(-eij8|P=HCyvL?}NiRhz4V3H4+rb))2ccB9ciWLS?WQN^W zPT(mTz8B~sAx80&B>sLON)#-(m#)9@TmbJyu#(!n`HrE>x_o5LGmLwS=iWUCJ z$va2Lku;fU^K=pV9ZU+GEgLg3-USwpMBrAY=I;WH;6Yi0ua;BiM1;*Za$JT2 zc${@R6iaXXO$zt4A$&3Y+u%vBVd)u=eplj0mn}wMdkiGxc9f9m>u^Lp+UW{zO)C4HEw?2#b*6zx8Zr=L62x~jL8Fw9ewU#DT6 z2*_z8*r)u>2`PabRe88wRb&m|lG7)<>6lSQFjIkaL9Q23Uzt>(=JC^`hy_&9mX3S3g ze17Fpzc(+phd*xqX+PyJRJCh^kJjAyxsC#TvjI!a!vE8&T6n(QgS`~w2z%4=KOB=O zOc^0f#tPmk7=p}tBKZ9L2|iK0{8##~GllmA*&iR^$fziT2@EISxQ zGLAN1)CgHfd88>D^ZAr(@ERBCxbY(--zfXMfN5Buyr+Gu)4y(Soad?6Z8R#)^yd-d1Gau#{Ee~Msa8J!f(4)&Iuag*7dFBY{{PO+n0{8c6LZW zXc0MwtoFq-a*0id_%Bpyoo9GGkr%%MVY0J2^%QkbqN@4u?s?hn+AH`F13?4^#A;Mb>1;*iQ3? zWVEXstG~!WJRHWQDK;f|Fk)?ICjzhBxTBHAdvK6uhENYbMuF6@1MTCxZvsw3zrQ$J zOz5FIQ%d)e#61y$oe{ac&>Lpoui@i13&d%*oI~2`;BF^@9lE)TaSd!h)6Zmvnvkzv0aQ!JPe2 zQYfgY&U8F5gc)97Dyo>h3{uNTN;HUU=Ks(RQ>BZpSyX6Z0_y8r-Rw;uq9K7`?XU-A zN&TrP0B4W#eMpL3Z2WUCwyS)=%^hu6L{T=aXqbHpi8DML_%mjFVMj_&iaJhG)D@fl zqo#;3tB55bT78Boy=Cx(j zo3jc`p8rPKTR_F}E&ZZ{Cb+u>cOTr{-Q8_)Cj@tQm*DR1?(QDkEl7Ys2)UF0Ip25B zefPa@t+!Us(0g{%T~)hk_m-+(&9K%l1z=o53Xca5dU8UBr(u%i*&Tki4>N}JEuo5N zC)XxjPCN}pufXoP=W3PQ&0n}ZgqpJ4D34aE8(!8Psn%03 z=)^oHDl?{M#*$Lz#s)xnQ-!BRVF|X9F5H(Wt6i$v1kg=7eB>LzqO~iUP2*|&}=PoYMg6(K!GRgs+J#QqOoi;Sa7Q;5Co|fI_S}ucxvP=_qicnw#6kW@3 zkp{zDnL_T3_or*9ODt z)x^)|EDIxq5q1-Ul-hD}%ES%rB~f;2FMx;d_CZAv8I*Y@WU_m9Dcb7ng$K)r#ymf* zI8#4L@%SVu%SJZZ$>31FO?neEFnH-NaEu^j-s}fO4J+jH`q<>B1PPl4Kq8r%B>A1f zai{)={(nNQCWh?fO zr|<&7Sx$3Wb%jBIFqi^ko)!m~=5g}@VHJg6q+EkZR;06zVq92iQDQG;7oLS`b)TU+ zjjnfkmIptt)LjYP98~MrQP7jbywS>2e#pU%vVb`Vhqa7F$uWQ{KUD7{wr-WD&nQ$F zt}XSKsR(mZ5eL|Po0c=OSA>fkZ-VU7sDhnDi@(`5{-Im%U?#DxZ)*u;oMs&{9+66s zgHqF{XSq!cPg*Tsk_)GHxiYVXdpoJWu}rM-;SXRc=uT+C!&kRxqT#Kj^F)>I%8)7d zm8@U)gs%V*7_@Awv5**8Z!o;HHo3wF(93^F|Aa#vKs$jZMHI{eyG9W#JK0#=%Fr>| zAH=8=rpo0h{az8703Fi#bn>9fYGeaU<4fo z+M?-Xb7oo)%YES`ZN)L{Tu;J3dSb%=pKiO;V}AGG-o@yjK0CO>F;WCEj6IK1yzXEI zml$D+C()I-XLI!PknLXM?%a}~uhEC1ho7=qowQGOuH~KxD4Bl%GmJhZ*#4PduTy0% zXqsBIxQn=+Nh4kQ?JKP+V6kE6n8^;F@FtWaVUcwm*%w+!qq|{if{&K$LwJJbS+PoF z!_Eh+nDa);R&W;PQ#a3U0zO)RKLA1Rxf)IcvD4d-THHSXEAh1&Y@u4Z`90p_qHTTu za@%Jyq)S-CLs`~|1+S#2n_gr)W~xNkRC**K$ncrLSiIMD3^lPKR$or?p@w4-i#kuA z0-qn(hNsk<_f<;43*MXVwP;)$^MdY9UmSHc<2!!4thEy@KB5?2m;elX|rt;kR12=94?mIjUMAP zOg4QW=h2+RjQ$pJSf*D6<$ltKTb76jX+5MJxX*U#JdX|V+!plLGTfKBJec|xGeaJm zXqsrJ{<5c>dORc-3U3+EyV8^jLq{9(AV@Z-^UVViH33u0HA%YOPO`$84ROdpT=z!W zt05xj%Bikeh{LjBGBR!m%91CY=FE?6RS*M~8Y5;}G*PhZBRR9dXsYwi%r@AF9g0(C zgNf0!9HjYKcDaSf{NeqaRGk7J^fs(-{#Qw|50N>=otYS0HDr&g2%J9Fnx?m9mjEr; zKyr+bcob-gDo4?X&JokwI(!rAA?O(Pc!sP|`G)+1L$mQBof3flz4^@q@+_xB6y$7J zl2$qbC-$hc>r(+3V|10+fG_ikGS47r9}YsZUWSSUQt7z~y!Mu!h~2FH-d-gUaGBOK zI`%oO&W&ZK-eOq%b^>pGf^^2@9JVX`o7~_PkTvusM)J{F)wEraBlmXbRfhT0{AK`I z-!2**CYNAtON9@tv@B{AJSWHS9ePnilhnQfAxrWQkl-gum=t=kK*z66Q7(M*M%8jH z%R*ElJFvGBOsN*vCDg>qDE(}>7u*qQrZUPTnIcC%7|<0PK)2SJp`_dLJN);y#t^|u zn|Gu~8uqt+g47@QA(kT)n$%oQpCZa3&w(9@Fh9f*Zum4O{w% z;;7-1J8)V@84Inu%($l(UhDej9k?!_lhP@$G`@Td_Va%I(+Iy}QBJffXT2wy99+UF zsz?JMP&=Ve?2bakv0D}0G>HXHdGrX?IziVP%^jjceWy?q!8+A7=L!%&A56SrHM9&0 zl3UT|L%D=uV~dwAUk_7j#sU_wp$}tGO1G21#|`R)$H@@ z;lO?X1(A?oKhb=ZO*%DCc{BqE0StHo(^#{hl7om5=q?{KL$N@8tL)Lb(_9Wc-<)Fob6JDKd z?^EL=JS+VT<4mX`c*h%urcs`z^N(bBxMC>9Qp%)pG^WZCQJn$Gobde&gTx;wY@C60 zxy4dHTjI6Fx7nn31_`#fBqQ&t@WRqj$Ui|0%9gf`%O~Zt?>`lsxr{5u$dQ%0 zx1OA$`6v(cXKa9X*VjYZeBL#!qXUqmku zPL#k85!YCT3@nFG8(o+}j3Oe!)vkg9a|(_>ASf>HHA%qGeq+e6xm#-gA{i%Qin8f*G*!VAOR`Bly{6&{#s?qMH^)GH&P^Du_aFb$f5S1zN$R@JJ8ro9m6k=!1e8=?Jg>Qqy_%Hf7s3;6)Dh z=Qb#9p9=7+0>>h7E)VU7Sb?km!>dB}uU7>pQ3B!O<`nI{$lqyY*jQW0AAsS2)@uAu z{2|2&Shva(_j+DcoRI@4Dr`6lTzAt_yA^85k4QBYhe#9%RJjScBa=0bQg2AYPnMjF zvMlgDl-Z)(RQW3hLEE?c#(#DlS+FU+&J`lahDpLk3sg91pb|7j-Ne61SD>;zka&Zq zm$v3K1|I9z4d3)!hX}vd7RmoS;xmw(_m-M8krZ_bxBLtNa{WH}MSHZ(!9=bhpgaDw zZRjpU*69sONb0@3uE<}oH}>uImFwa1Y#txVKJWa&^hpKmI#~tsi_D zOKpL;&rA^S`xVZa5T*$`j8-27IWSwC{>mv=8$aDz^+iCMcK;;wxFvRmIiA4QXCQpDaY}!G^hp-#`q#Y5y;gC0FC_f=u zlPn$-v%BA6wgS#Y2-y67_lr%x6CKCs3G`8*U6SinzZE+l^Vtj0T1FAvfXZwFUi}txH8QiGXsoL-_^E$5FG~n??LUN{{}|KN#6T zO+__B%BLbZ@}j&~MUN1Kd?>!1zk27d@zYC?u*~>~&@ybPCm!!PiT`8Zs`t-OqF|S} zPx5w^g-2P~tYXblliPiCvm0df(DyYi$pl)sS(chRv;q1Ck-k;B8M3#zti;f~jt z@@PD8xb+{v1wA+dixUkTfdvHt4F?Ge1%LtvVEq$;1r37+4#8rB#UlO0!paU*#u3KE zCgTthB^NWMbV~SF22Dr^h>zfr>s1&vkqHy$%x>jf^LmaM60%egD_e7#VoVG;W8>|* zqiw^whg&)!eDpfl*{yzO#Z0HV>0qQo{T%cinKJdU=Z#F8I+Qw0J5PI)mLj%q-wAw) z0rOG)MsPQX?`Nyk{=WI?VuM#E8=^rnT&%=mBQEsEMP0ifI3^3}qP9U@@uFx!>`4v2 zbk4=i$pslPBuimnVr$&$o)nQ(REzbYSwd^vrn>gU7A|~v&bqEmiNSgXgx8badJxp4 zJ>!qXT6;t>Z`)1G6ds$JBI%7#5%h_k9tyNdR(PNVR=+ITy}emX!p62U795 zM66??@Z~c%n6cXQdu=>pRaFlw+_FZM-5wHPhGs{T18d{IPr2m74(d>;UsPcoj_U?cPs;H^i8*FRcAKrB1=Uz#>Xj* zoE(BG&mvzdtx(;Yy+W|`{QpXC=&$sKNp7X-?lJh0qbA2?>)UhHX&9#6EfSYfPtt^; z79q<6b|3yjh+Kb#*l1RD-Y9gfH0c4)CsGKk`S33Z8vK=DSNql{13ID72~d%lyfbhS zdkO#0N-8e>NTr$#ycJkfq(*dJA`p74JNHCv!B@AeN9T?4O1xThWrz=azZe7%9z1^+EGo-qn^-d{$SNrTJGuuUZYME7aa@9;)JZ(<-1kAAi(jg2Gdgddm^&z(CX{{~L;7TC5IT19E;a6pj8J&|USY-=JzA-sECEIeCcdN_h;b+eZ~E4ptm^Vx|NsjPoFyW&HlS?N8+@HZpooFP1F zSl-}w2~w0Qt}krV;p>i@{l(G|5{tchgxZgmFezdht2+50eJ^14J#W}9?J_$%k=_8)k+nyVRQew~Q&F=icqwTq=X%B7kK5{?s1Y7k=~TKKIkJD%+-t#g4G^&5uqr@*q9@>Y<|sHe zz8^pA*S2)fXy|mL9M%5{9PWG4S0~TnBk;;J@Y6jsR9#wlK3aJDeSP^3R47-#Yo_j{%W?rwh`H-ZYVeaZJK(nwekV{igcgP!FswRKQ!1v zu*QPYPVEK~Rjc!94OTW6Sl0Vtix$DFY^oo1K(ZpLcv#6pE!OS%Y*S2{D1984^1Wc5 z{JUCjxUk~Gr)zjjB#aWM8mJu!&~6Pze*U-LS8kYum%Dq0{qxgfgDt%J{eA~V2bsdM z)Y>D^1Sz=}gN0DN>B}7XIJ}_*ubNrX9AM8gwmNTC6n2>cQ|Wn`?IQ2lVjI#ccuf8? z@3myDr+mK0f@zS_ioyvDXBHB{>uO;0QvZZL)pvjwX)0+%G5Tnn;HJ^R*Mzm#5oFo; ziAv@Z@cnbH#a1|cRgA7HloCqt0km2^x@c!2-=(OvScj$eaSlC4Dq2@PfNkHO$(C3 z5fZwdh~mfj1MZ(8Zyl8{#+Aq|%#1WJ zTDtR~8f$tHT@>DV@6})fkeg&ie&P`d^_zdwDY@L>Lq_UtZO?-)MF|(;N7t*7i)U86Jb` zTv~#r&8?=^C8($LL1WoQ2m*fgj3FvNi3p#k9jA_Jl0D=28CvY8Zl%IJ^mhm1G_o9L+b`ZO zsREn&1mSuihjP4mm(HL5}(0?X$mJ5kX8u{`_JrecCzqt`C(I_KsMi=Lm_T)p#l z@74-{Gm!m%{z$&XF%#AWtSd3|IZLpy$54Vuh=9VK%ojE{g<-Xq*jF;?pw<& zZZdE4%WVzq?X6=9udCyRjxf%|)3cCFGHS=N#~<&#U)Ppi6S-Y@HHq-`OOhy4yK0`1 zm6{3sbHk_YGHmmgTHJ;{aUOwkx6AkTGXZ&^95*9VLyrD!b3+1vMye+Q{og2Fd!DeD(O@ z#GMAiLz^bdVqMU^w-moue{+t$XpPoCtO!aqxe_LeP&jXIO@R0lCffc{Vl>=Io)*( z(P^-Lj8J8L>m46P?LK*cXwaeS&_Vq@udb{1e>{p}yWT14`y?n`a21oyDPa0&-NOFs zQ*`F%y$(C(=HLVU$?k3n0$m0S^&1Xe)RP+d0{~A;h0wtBP)Hb9L>MUOe`cis2mmA$ z8Y&nSLf=m7gYJljwf5 zhXXsg2_7$JR1ZPn|G!@AowaipoK|iZUM<0g zjesU`D(WF(hOwD9jsl;?Od?JfGQ@aO84;L}Wxhaa)jR{oS9llrQ429V6qEz_E?U|Q z(N6nC3ogk4UgAih7E8$#3yrMChJ3&n$C75*alzK7YL^*MgN1Y~;mnPpqR9;R1bIs+Y5cWOst;kSP>7p`vlaQ~{h=U6SwboDT z9Ha0wE&jR!4{#?i6)O5$1Xb6RJBYIy@@fP>RyXgm`3a%K`bId2iH<%18(^NJ_~V`n z^Io`ce!l)+Pl;|atA6?yYb5xq%t8`hw0t3Zt}%_^2BU-DQw*PpB@vo1ZMn``1lFb@ zh?ZG+(4B3b^5s(w6e05q0;~s2Y1iwuW05vsVw7zCr0pF8l3q;G{fge`3p)(ZnhlVa z4c8W`y>XeQRmyh@m!BoY@j~|2c9yOc;%ne15(*x;;aB#sf`-)^j2rL?8WC{wmXXcb zh~F<^uvuV{kKJ^B2Gjufeq=6~nS{L;y)ma2|Ag@-A6D7qe#T#$eQFynPwbZ3K-V2h zpl&e63L}}%uLUqFeKwSHmu=|BiquxXv(U6&L4b+SRtp-ob{MCru^M7(Hf=W(^WaDV zrxbK<8MEbI5_P2Rg&es3P7iH3xWwD4GvLPPflEczZufHAmdxbgi z+B2{qv_Fy`DZLbRREKYdgniZ-C4A1ch zU1-#JBel800)sTv7%#R!jz&xKBVv#=(eC`~vF_?x&zD&k!$qw8pu!i~=wmwOl=5EH zB5&E)|9uMnl`Exus2lBZi8CxIPo%Gc*rcKis?FD%ci>Ca+E)GTHhXb=RJX`#fG9+)YDz z!=}8$C0#~XWK1rIO{0t|0*xw6ikeT#J{XwEzlsjH$lBC*HI(^K39@ne`^a=)oiZ@edc`tiBOeM3p#bohJrt9Gr#uNH&dF~6A5IC*KH%{hEw)7uy~+GHtg zVrRNfd`wElk?XH#ZoP*9z?`RbzBQPKrkjE{D!iEoU_JEnm80WKqE3 zhsMPw{D{6N5XM9+#S#98YwK~Bfa9=(;=5)K_7QShYYui}|3ZVJHGV{2`ClPsdC1{Y z$(Mrp1+PD$iu(|xh)3JLpVPQlZ^9pPiGf}Q(ZW**POxh^e+W^I?t~w;Z_U4@6MQB~ zB0Xx4j7Chzju8gPf1n`D2cf6ycfhz{Ed=K4R?`pf^9If&_1h0 zQ~e~eGB}rTElFg?*0Rf_q@StzYQ|P&K-{j~8+~$|tYeF;y=?7G3-k34AnM?&(Vf29 z~%e(~sow#P{}S4R?r z$V3=)|KtanXDljM@WgN|I#z@H6Dl@F$VJv^Z{JHbU%$SiT7b|GKe^Z*lnLjyf)^$* ze-t7U&KTHug(5QqKP$4i*pmOX%N1#;GaKZ_&tJTK6EA4=9n+B z#Pbey+X&?jD?_*!?=N%L(XeL`-IeedE&Mm-0Ja?Y&>)au^p5nR<*0&Ns3L(zhr`^+ zPY0(o^)d>c8UEPM1jz}2iN((aL)ZNQhzn2DnR5jW!7wJweJOZ4deN$ldvd% z84!7Z`7n+7|9Xl8?K%r_MWTv>b2Q{A5yT+WdGH6IN%D({`O)MLpz+^@kLzYQ;wG=? z1qwIk{0R}RH~sz*egE1~fPjVsK*4-~hWOXm4H^vU1_OXaMFXN^V6w1dVUx0P2rGYL zr4xUd(LF%mnW_6V06rl^(I|BHM8M9ON(0OZZ zw%h#dp6cK{J$)(NWi#{M7N0I1oyHz>J1HlM46(omdCTc9-wpTd(i09$ zNOs2*5`iyG#7!wdO*p`&6tyk*!*|b&8#$N;G;E^9BCb2a)^P|Zq9IinDYui5{T^?0WGBxO>`Em}0X3DYC7tC1IYFYle z(6nq@19>^_ggU6YM|Gb>zwRaS3@FXXK(Y@PSE+|jx9x_Kada}vYfEs@Q zDm61%eplGyUpx17&*bsS74i}E_4a4nLW5?hjv6^>iW3*d&&`vh=9kz;j5wZ`l|$jt z>50#F)>>)NwF?tT9{PZaX*aOGCOT!la5^2*mDG`0gq|}BIxLfd*nGoOUL<9c zbv0?g?NhBR1|Au`Yq7)75m1Y3%$fF6N4zUh>1171Vs!WCJ(yZSZzeV?&9WLD|!cQk@3N5yA!LvX8%>3kPsoHU_A z*DSS}>50FBTSe|~tHjQ!u>*~?yEltZq!W+DX$3Ou^tV1q#K_e1@D+|GGacPj#(KhQ zqkit+Ok?>OAQvf+ZjlTwL+`h^w7@gj{t=O*EY& z4mv-!kny!+!z!frdtXyCYaSil4G9SP9?@^{dJ^{>2dHP? zR(SQ=@g74hbAM1;?$LES%Q(P0oA5OQ6*qQz5=cVOKGsigj5$zBpK_4Z*eOVevdg@R zxq3bJ&wy$nhCaX0vqe{H9)DG+->)X4#PUaaUakh$Xx{Gjz;72{VtI2Y)-?62Vd$0Fos^iH{g>KMorU%iiJbaKM!D5Fb3F~A+S9$RsN9hd z+n*pKT=YxW-VtzO*S!pI+Ub>@F1p0(uv)U?1_{9Th5a>zmNokSGK5|N$@*W^Uh@&e z&gR->GpZwx&rsCcn~xamnlCf^Zn_^4yJ)F60!kT#8o)gy6G>V#GJT+owVChlFw5%UlQn@z7Qtnh1|<>2ukCZCE68d@rDn z4MlPfHms%k5G6h@B>Va43NQVhA^k&#+a6h#Dnc?tD)#WB0`)o4%;8$yB%UgL)G3oA zJK3BOvdUxBcGGz)Auuo0XvkOTapf4Z0%-)a#&w=(qz4JM>0ZJGjI1QwQZQazE2v)m zSpp7YmDVg#@L;PvGZou;wbR|_DI>9Jo#Ox{y*mr{EB}J{c#$2e6oE&%k61Jt>rIrT z^n6^vLM9(`yvgVvz+q8vUo#p@`4{10v8bq=1@~<3OpKsxi>5GELJFf^1RN)pJCo|0 z7&`vK7JD6LFd{muIoe@pmgjtGws^>h4Y`^&Flgh+LPN5!ax-DDS|03206aCJGAOg$ z9O9_h_?8W;O+e)3noPc3=bF>0v`COWZChQNj(^HJ<0G+kNlb1|wm2xqZb|#Yz_g9w z)jk}_szB>@mrNt5RbN80k`AV0rJIVsDw=wWgjKQl66oFRIU(t~4+iG=ZC)(MM>jxi z`D(5Jt-|7!X0sRhj~oWPK<*cHYUWcAUyQ{?;v_(+RYMv`x*Jm-Mz96z3R9t^wiXFj z`;9S0o3b~k!!IXMR3sQC+~b*l`>%G`+88r}c>Z&;8>6g#St5Pg-{tN>J6cE3@(eX; zPz;JfO$X9}htog57XSX#(GpRjE_-t8lp7T>>5ijaGbNa9GNf~+@y6MJ*{RCM&rf2S zJ<6M0t+6jw-w;9cFhIIA16_n~?BE)fWmA^8s8AkIrXP3wE1D%H;XZH9>T9Hd@$pdr zC|O{}JI2h+OnVlmxl#HVn?6yuGOnhaYEbfsWei$ngji3LZQ5ZJ^V6sChB?4PDwz}v zqZ;Ug;i{pAkG%PnEdT9zgG|k$9A<=#rp79|cFvP+(JZ%ltILOoa>^h*SuuJFPyV7c zDke=uT{1Ekg|Gs97~2sB)&6HGrYk%K-Zq> znhLf>ODW_T9ddel3HYqWNqXJq3F9?>sEj#tJYvLU0jYw%|zYRUir8~$++-)D8M*WlNiz);jY>+s%E|N z>DZ}y$O8{gTD_+J0AM5}PRC!c#ikM&u5yj%Uq)Rs^@Y84K>@k<#j2fnW~mkas^yv2 zuQ^Y@6@C251p3tSb}Qx_mrvU+*tZ^eu3uxo6%y`R?1?pR!{6PU(OP%+K72R5lKqsmCR{)xUu)dZkXHvg7h;oC#Hpv$sH_hc@lqOZGMc6 z?wacSY9+fia1S`Q0tv=UZHoR1yALsi9_|pW)Rx0;eW3JT5M!p2e4J^$4kV zc08;a^=Oh@rRBl5o_V$~^EyKuB^6p#s*@_VZkc`6BI!snjt86945Re*D--Eus@uLs z+@ZM(l~nRBD<`y(1R3;~yI`AnL0b%ZWb#b|8<|vSlUN=U^4BXmU!c<7z%X z?%CZ`CD}`2mnq^7^|^1Uz=pT#Fq&Sa4jb}bZ&F7Rbl!v_-}f;C_|ej~36RDONSEdc z)63ZEoBaC)p81T+%X34@vxesSP}@c_HMZt@>COGx{<;DuQDxr8Udo?XYH2RNd0yJA zq;(n_zGRh>Uj<1#ERDA`h85#Qrzre5Vyx60a|LRcQ+;%}x3k4Zv8bnSDcwLQ*F(p< zgCX+kxA8%1iT60uXVYud{k9_&Z2SPst&bMd$BS7S2_Di3@rb`lGENP;1x zOB@@;CGU?#d z{T7=viWw{Fn6ySuxW=KgseC)T+xiDUT3EcIG}EZ*)9zXyR%yLgt0h0Y@+p}k#mI7p zPiU-9$ttC9=9*pYUCA>592?8d;Gg#aJdte&WgiFCJ69DI*U3&cz)TW(uYqGvHEbMe z>TySwR`441M!U!twnFKsvECcBu$-NR>?Dq(UrU)M!Or`mT*tFJ|R={uh5Nn6vFj$Rxsm7+sM zeI^BOS8V5cS##dG+*+&7Br%UX-D}R^9V@Hr^T=Lbp{ZX*^eYwfROD+L!S7Nsa_?GJ z?+1Bt$%lIn-ZM=gu-DBJ2d9kaTeW|)4=`EK`e{OKIUa=OD^drVN=#&*4a%#wS&s0W zjYd}20@w?%gOfbfIZNx-lOE;{vylc7Yt0~tfpxzP=LpF zHt5=j0D4$*1YDKi$WOTSkOI{QPAd}TM5hQB}A)j1;A$TyZAS$cbg2xGnV7ftz^5iw zKjH-Hk3J(`$MvL90A71adzZ@)h%ZgxsQcOJYCg1K$plYtF#PT1UYb8CT4eOBh5LDV zp8owhu=s}na2~jp?UG-PmlzmW-X}lw@~fg?bE~{~KiV~}F3NChw(fs!M5>c84@o=Z zuueS$CFe>3i&_SB>}!cJH!akuF+M4!D0y=>nIwn^eA|L0=KDk`WXHfARpZy=Z@7As zdWZOhqP4UZKTzHJ%M|i%JbT-59gd6Ji_j&}FT zFT1|Bb$sTvp=N4&M+49$3WO}b8oc9IYqKJ1$+CvEN%%KkNmop(x;4G3?{p3t*beYM zR&(N3^r!Kq5W9(siz_u5(*F8O1XqCpP@jV1x&Sdhtc?*w5wBS3fz#Za`YXm4yu1%{C;K7E_4JwWAQeduPZDwF62*>o4ULj_eP^q9 zyK?Jh=oxJUM$mO{iB=q{!l4^~ZM|IKVHj>2)spWo=~G}`8qzUsZNT!UY?kfi_9#)g zu18C<2zMOI+P%c`~_RU z>P>%VbIcQvjQ_LxPCL_op_<$FyQ^Jl#S3F@Pd0X4Mjt#`-C0&YI+XU#bKLm*$fwI8 zO?dGn)7=-wS|%lAqlTq?9YzxBq4wFt6;6Iwrnd#tx00We3U-xwrf>MxppWe6--BIP zsd&+{tD+k7&e!g3!HIbFl!*-W4j*tLAQX)C$;J86qM?-~h96Ao&{Zw+Y~;vfjO0Hw z4Vn?Xhy?@Ggr!71(W?^Sple_Up^D-@glY?w4P} zb(<5<)|OVGRM3m~em3<*^Zjfz-6Fu6ZX+>n&+Iu??Cm$)I0b{-)PWb#B>uYPLPEg6 zBSJ%efcP)BTr_lO@D8X71{s@(s+x&&!vZ;ru&A<2U}8aG;{d68(jaC~(LM~jv1vkb zlbG4R*VO*m1yn zNUS(Z?+ZH40x;@vlM?YXtv~)&tTU1|*va`ywlU6%4pg`DV&<&#(|*wo{mEH`4M(W~ zqKu8z!*uGZc`EP06_S9ltD;djxWG9S5N#a1n>=DO(X*{4M&+@S^Fyj~**@|CCXH#@ z;Uwm8e)3f}8DKbzHE(Dlu*5y}zdwLoJLiM3Fr_?@UIqv}b4aS85C_!qMwE?V23>q9 z%Kmiz% zBI#^-ld_G?4{6`$Ijs)=Iz5$nKCem4+vK%KFsg7niRqqZ8bibV3{#%eiWqL2#kV0M zwn?u_Yqm`DEjOCDNo!kq9ij+B*#wuA7sJO$1=DU)LulJtPnXYf4%@EMq3W?2|KdvEj*4U($6&Z7v{_58Y$(b@ z)+l{o$2Wng6ZmVsK~>}u(|;;A;DYquY$pE)oBap~UAeOKOgiHB9;z8$HAOPD@_n|a zf@54viUUSj(HB@XF5Vw6hq9?;ta6>dEpuY=2K0!N$4L&5F$EB4leM3!|MuDKOL+)u zrQQ`{zSa+|<7C?{-?|n(Bqo3Bx*AerBXP)jpcK0Sj%N6)3}t{~crJY(8K=b8r4*Vq zMTCA^rc_na6r-6kFzOfS|MEcGzI<8}`Xyn@0&!zzbbPLLhRFEY-Oa>l(gDd_xjV)| zCxy#iJc5%3ps9eF*9m)Fok?zmZQ3jh&`;LK$=vuHS?lGY#reCiL*Ylxmc{Ruxe`A^ zqv8{S^CPO?a6Nb(Y`?2=1j7HDy%!slb|a1e3sfrDm`hSyvV0x0VFCo(_Ud5jm{Kt-w59*5 zb$tA)=pg4S#r0R~!s}0tC)Vj7RD4C-nL?FRunVjrC%GCUp>4^E->E*;nD6`GXBW)h zCR_=s&El_r{qpY9N4HLD&- z>9G{s7#}1`TnT;4`L@TGd2UE&f55~=pnWluj645w?){Qq=vp7)4w*E2N}{=VJ|dfN&_(5b&gH(HuQ`=r};x=%Hpvku^QPCjsP z9yZA4D`vLGK*Ce%F(l63ob@2^>=LG0yJ!G_XgLOsHOWY+_m9(Kx zadThtSgElE4ez>^mgPOsR(O;Qo9_;z`efN9Qn2VR7h+FQr=ssQH}=+Xr!V6qwx^4I z%*>0fE(8}m9c=HLD_!}&B{y0^6X#m{wN46O!@lHFD#S5sp-QjAV|+oX*1iJPXtO+d zD{@E4Cnpan;k*Y83#4i-HreSa`A4A3)aA8vkhA z9{_qgfn+7QSJy&IdniGY3~&y4@_>!@X?>xI7MdtTtx*xj7gyE6e@k>dHr1OB2>%~K z=w3_oSN?Dh@8QjC(Z<)s5_4-4^Smytgtjah@EqIM{gbwNlGpJ6RsV z7=d*CffvhMaFR9W8j^6R+ss?_(D9W(Yx|*UUfXKeSw^m0v+M?+VA3=F=6o6542*r3! zspTVpk5SNQ)%dCjFNF^Dcz_ygSp8%yS5T> z#_YE$<<6e#kZAmv3a9~c&||DQj~KnuCuqrGRNed}PImnds>RVr&23V8Xwrr#oXQ+} zWhOId^0^9w^$p3t!1fkVt5!?|QfcJP#sVh+VPn%Cw-vB*NGHltx9mszf0^ z`4PE92Kzi8zMeFA6iIR}8C{ker+$3}4bJyRh@-lu978n1=6GmajpfQaNlGEZq)rwU z0A6)^UK#*-l+^N$lj^_tdxe0!vSlR@+A*%)6##~-UY36$C-`5LU1>NJY}+2$daa3J z9!trLWsqv@j3t?2EMbVoIzsj>#A68+VT>`Dq>^Pu4Tdab>&Z?=v`CZe4U)0TGI`NA zy~q3g|Gt0casRuH`@HV!Jns8G&Xb&)Xe8_)t2<+f+(eE9E8TYxBAcD@>C*M#SkMX& zI!HmY8?|fzTrcyGetZe8SASt6a~|S}{V%Z>f%z})W&f&X#8K0W-a&oGZ;GV;0F4$? zxYm;+9i5_RE-B zj&jqfkP zX(b)A#Ga`oyt(VkO7Ot&R4jpEqyg~bmbhn|`4u^zhuQ*ty@ab&=*-C;FS!Z% zP00}ekL^c<-zClw7}6GmMI#NkEX_maIqI)%cMD0MBlki%Th}}bugJ~G#fs0KW*2WH zzF&W0Iy3~q!Y7WYC;h5$5~;fAh7Miqgo6mVM(@4rt-RR;kU5&6U;FRV0_N)R90FEBWm}huS0^1RH!+Ql>)Dd)-k!nz{Y;?mU(Ll;)4vng|hhX?kp*8nw^rGH;-=Q$fz7Eixxn6FY7;?n1! zm$H@(k^hEWjORKKGudEUuQg4RE_`cd4t}@vVkbsc=hpmfsmncRcPFz*EdGT!vvt9E zE?GtDxNenpqnuf3#(ZCM7ncyZG~Wy=lvkdOC8-YD_GM7L+vjB7M_8(NFCdGL5zn0^ z64xST;(HL4;0p_A>WxmOB>xq}@pQ0;qbbH!~>^>dJ{hCjTp0>F9>XOOg#lj0>ED3 zQg6vafv^X(s~S%o`=MZ%JfCx9f;dH`LSXp7pl!wbLPr6CUrh?RJYtcx=#()0Pw5YT z;=qn6cT*{%L}~Kv0N<}oS*1l9X5@1sZ9K0ZrSK%Ly>W}c{;dBaM}I>mv#Etj~Ewh%m_!Gu$?c;G*lAl z5J{~Ru37T3f$LLxXYa7|yFrP1=M2m|LWB#+!QbKi@t~LE) zT$LN_07xkKqJP@Erg4`+@7Mtz{RWgb^=*HFc5IN_i|PmX6=OsL%Q~F?dGabyo0K6f zWbg^Nev9bERIsIIcD1_hNlv&ck(!V2!wl8M$ldw1K zyMH;vvYbH(K&4iD3#u&ESFeY5 z71fX|XPe^lh4z-i#NHdJ6zi00Ewnsf(eo^XsqBo$uy5`gwHfhp-s`Qct-w4pWrKy| z+$CXc^fQ_`S9D5C^JNY^0vC5)U^NSRB&W~Uu7nMJD1)s2$?p}VGjoHYGo5hTsTi15 z>Et!(wkn>i3*SrYX!rHa9@Sn*a7J*$FPew=pzSqsB{tm#L^F*=lvHq^OG_Y&@Y|7M zm@AvWKC0N>vwm;9Bd{hR9^|QiwN2ME51#*cyRCX48itr^MYbiq@% z4=(ktY`;>~lh<4L4M>(EjXNvOgJjnU_Ow^~;Zu(PnwLCg2=hFuEAv*Eo)9TF5%)&8 z)l=H8&gLB`@V>7g{P)P1E4R;-k?^KHnw;5;Lgs3g>Rk#NIcqldK_My5h3%)}*DeDM_3+e-(|7+*K~X1G(iFaCtRA?39O|vA6_50Zd_Fh{38*N_DdmOK zmxU-ebBi`(p9y6AXGNWwMpMF`-+6K#>Otm3kO9Se7@)*Ee;aQAh!h^&^zaQtq*Mst zxk}E)BlFCDxf9j>OzRZ(*Mh|@4~~DrEd7wcc<4oT9FN{X4-y0#;dg}qs!VunMV`J^ zK|kMtfQx7zQ^ZnIZv{~aaS}nl1L(?`vp>7!=DKg0bmTauLxEE*1<=0>7&Euu$j+ND2K8G0TYxmgMx(@$vZ8xZ1?{SGOusNl(auW*Aqp5YVDJ+06E1ch!KR^K@QHMe!ZO+s%u-(u8yt=7~Xu>#Gz zG1hB0!u&;y>+J`bP^S8pmF!(-PP+CDPR6O~ScgYQ;mgFR|K*It14@*i)Um}04*kU2 z8_uzmlYH3@mhEi0By+~)a%bD0<3k9#+l~NX&fy@)1aGl9)KWaxfEzF4LDsZELHBzD zwz`tKL-(roRVBqSCtctt>sesRcKE^84P$=J^r$baw0)wpAylw`A6YmB;nT2TWNt6q`#w zbji@}RbsG|ibh~gY#7({&YjEO#bll;Ak~c4C(u?LX%uTFiUmTb-3}Vx&)z$sTTWLE zz({#C$(7?!nm8>&?F27MXAPwnc0SPE@EqFaxp3WGd2XL1UB1*~Y*L|Xad|~7dV$Vy zbP$z>%hvwU8K=~WPpSF;S6aNQEdjpE9uCU?hE7zqOG9l`8UvMkblzKUH2be^y8jp& zbC771OK}nw)19PaBi-tbjGh$wS@7`7cC0f?gaQ@E#vY0K`GKBBT^l>z`6{-Xat;i` z-hwr^^5L^=@N3$Nr7jJ9y-uOal1a*MD(gUzn!@E~>N?MZHOw!oj7G@~qZOVq@^E@^gVoL`1~+`zrg4GH=q zhUR8rZV6ybF}5Kn|Ijy1xVyqnCbXR|s(F&j6nTT2I&B@6U)Momn zl~40vbNl+;CPGgwrXWGeRz#vo^va=%#z!&v-QX>;r?CzDmF&wICs&t^gjb+HbyAlu zMj$fEW+#&V8gGY(KVE`c>Cwx4@n%%k0e}1*(>b4BUJnY1Zgl-#TGDp0Kkn<2!w5~g zvI66hkuJCqL^qCJr{ynR-v56Ayn?5WKTl%wvo~rR^I$L2G3XIr$!y>eANg-P#SqaU fgzs%Vr*-jYG(YMS<ttdtee# diff --git a/docs/bridge/static/img/docusaurus.png b/docs/bridge/static/img/docusaurus.png deleted file mode 100644 index f458149e3c8f53335f28fbc162ae67f55575c881..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5142 zcma)=cTf{R(}xj7f`AaDml%oxrAm_`5IRVc-jPtHML-0kDIiip57LWD@4bW~(nB|) z34|^sbOZqj<;8ct`Tl-)=Jw`pZtiw=e$UR_Mn2b8rM$y@hlq%XQe90+?|Mf68-Ux_ zzTBiDn~3P%oVt>{f$z+YC7A)8ak`PktoIXDkpXod+*gQW4fxTWh!EyR9`L|fi4YlH z{IyM;2-~t3s~J-KF~r-Z)FWquQCfG*TQy6w*9#k2zUWV-+tCNvjrtl9(o}V>-)N!) ziZgEgV>EG+b(j@ex!dx5@@nGZim*UfFe<+e;(xL|j-Pxg(PCsTL~f^br)4{n5?OU@ z*pjt{4tG{qBcDSa3;yKlopENd6Yth=+h9)*lkjQ0NwgOOP+5Xf?SEh$x6@l@ZoHoYGc5~d2>pO43s3R|*yZw9yX^kEyUV2Zw1%J4o`X!BX>CwJ zI8rh1-NLH^x1LnaPGki_t#4PEz$ad+hO^$MZ2 ziwt&AR}7_yq-9Pfn}k3`k~dKCbOsHjvWjnLsP1{)rzE8ERxayy?~{Qz zHneZ2gWT3P|H)fmp>vA78a{0&2kk3H1j|n59y{z@$?jmk9yptqCO%* zD2!3GHNEgPX=&Ibw?oU1>RSxw3;hhbOV77-BiL%qQb1(4J|k=Y{dani#g>=Mr?Uyd z)1v~ZXO_LT-*RcG%;i|Wy)MvnBrshlQoPxoO*82pKnFSGNKWrb?$S$4x+24tUdpb= zr$c3K25wQNUku5VG@A=`$K7%?N*K+NUJ(%%)m0Vhwis*iokN#atyu(BbK?+J+=H z!kaHkFGk+qz`uVgAc600d#i}WSs|mtlkuwPvFp) z1{Z%nt|NwDEKj1(dhQ}GRvIj4W?ipD76jZI!PGjd&~AXwLK*98QMwN&+dQN1ML(6< z@+{1`=aIc z9Buqm97vy3RML|NsM@A>Nw2=sY_3Ckk|s;tdn>rf-@Ke1m!%F(9(3>V%L?w#O&>yn z(*VIm;%bgezYB;xRq4?rY})aTRm>+RL&*%2-B%m; zLtxLTBS=G!bC$q;FQ|K3{nrj1fUp`43Qs&V!b%rTVfxlDGsIt3}n4p;1%Llj5ePpI^R} zl$Jhx@E}aetLO!;q+JH@hmelqg-f}8U=XnQ+~$9RHGUDOoR*fR{io*)KtYig%OR|08ygwX%UqtW81b@z0*`csGluzh_lBP=ls#1bwW4^BTl)hd|IIfa zhg|*M%$yt@AP{JD8y!7kCtTmu{`YWw7T1}Xlr;YJTU1mOdaAMD172T8Mw#UaJa1>V zQ6CD0wy9NEwUsor-+y)yc|Vv|H^WENyoa^fWWX zwJz@xTHtfdhF5>*T70(VFGX#8DU<^Z4Gez7vn&4E<1=rdNb_pj@0?Qz?}k;I6qz@| zYdWfcA4tmI@bL5JcXuoOWp?ROVe*&o-T!><4Ie9@ypDc!^X&41u(dFc$K$;Tv$c*o zT1#8mGWI8xj|Hq+)#h5JToW#jXJ73cpG-UE^tsRf4gKw>&%Z9A>q8eFGC zG@Iv(?40^HFuC_-%@u`HLx@*ReU5KC9NZ)bkS|ZWVy|_{BOnlK)(Gc+eYiFpMX>!# zG08xle)tntYZ9b!J8|4H&jaV3oO(-iFqB=d}hGKk0 z%j)johTZhTBE|B-xdinS&8MD=XE2ktMUX8z#eaqyU?jL~PXEKv!^) zeJ~h#R{@O93#A4KC`8@k8N$T3H8EV^E2 z+FWxb6opZnX-av5ojt@`l3TvSZtYLQqjps{v;ig5fDo^}{VP=L0|uiRB@4ww$Eh!CC;75L%7|4}xN+E)3K&^qwJizphcnn=#f<&Np$`Ny%S)1*YJ`#@b_n4q zi%3iZw8(I)Dzp0yY}&?<-`CzYM5Rp+@AZg?cn00DGhf=4|dBF8BO~2`M_My>pGtJwNt4OuQm+dkEVP4 z_f*)ZaG6@t4-!}fViGNd%E|2%ylnzr#x@C!CrZSitkHQ}?_;BKAIk|uW4Zv?_npjk z*f)ztC$Cj6O<_{K=dPwO)Z{I=o9z*lp?~wmeTTP^DMP*=<-CS z2FjPA5KC!wh2A)UzD-^v95}^^tT<4DG17#wa^C^Q`@f@=jLL_c3y8@>vXDJd6~KP( zurtqU1^(rnc=f5s($#IxlkpnU=ATr0jW`)TBlF5$sEwHLR_5VPTGiO?rSW9*ND`bYN*OX&?=>!@61{Z4)@E;VI9 zvz%NmR*tl>p-`xSPx$}4YcdRc{_9k)>4Jh&*TSISYu+Y!so!0JaFENVY3l1n*Fe3_ zRyPJ(CaQ-cNP^!3u-X6j&W5|vC1KU!-*8qCcT_rQN^&yqJ{C(T*`(!A=))=n%*-zp_ewRvYQoJBS7b~ zQlpFPqZXKCXUY3RT{%UFB`I-nJcW0M>1^*+v)AxD13~5#kfSkpWys^#*hu)tcd|VW zEbVTi`dbaM&U485c)8QG#2I#E#h)4Dz8zy8CLaq^W#kXdo0LH=ALhK{m_8N@Bj=Um zTmQOO*ID(;Xm}0kk`5nCInvbW9rs0pEw>zlO`ZzIGkB7e1Afs9<0Z(uS2g*BUMhp> z?XdMh^k}k<72>}p`Gxal3y7-QX&L{&Gf6-TKsE35Pv%1 z;bJcxPO+A9rPGsUs=rX(9^vydg2q`rU~otOJ37zb{Z{|)bAS!v3PQ5?l$+LkpGNJq zzXDLcS$vMy|9sIidXq$NE6A-^v@)Gs_x_3wYxF%y*_e{B6FvN-enGst&nq0z8Hl0< z*p6ZXC*su`M{y|Fv(Vih_F|83=)A6ay-v_&ph1Fqqcro{oeu99Y0*FVvRFmbFa@gs zJ*g%Gik{Sb+_zNNf?Qy7PTf@S*dTGt#O%a9WN1KVNj`q$1Qoiwd|y&_v?}bR#>fdP zSlMy2#KzRq4%?ywXh1w;U&=gKH%L~*m-l%D4Cl?*riF2~r*}ic9_{JYMAwcczTE`!Z z^KfriRf|_YcQ4b8NKi?9N7<4;PvvQQ}*4YxemKK3U-7i}ap8{T7=7`e>PN7BG-Ej;Uti2$o=4T#VPb zm1kISgGzj*b?Q^MSiLxj26ypcLY#RmTPp+1>9zDth7O?w9)onA%xqpXoKA-`Jh8cZ zGE(7763S3qHTKNOtXAUA$H;uhGv75UuBkyyD;eZxzIn6;Ye7JpRQ{-6>)ioiXj4Mr zUzfB1KxvI{ZsNj&UA`+|)~n}96q%_xKV~rs?k=#*r*7%Xs^Hm*0~x>VhuOJh<2tcb zKbO9e-w3zbekha5!N@JhQm7;_X+J!|P?WhssrMv5fnQh$v*986uWGGtS}^szWaJ*W z6fLVt?OpPMD+-_(3x8Ra^sX~PT1t5S6bfk@Jb~f-V)jHRul#Hqu;0(+ER7Z(Z4MTR z+iG>bu+BW2SNh|RAGR2-mN5D1sTcb-rLTha*@1@>P~u;|#2N{^AC1hxMQ|(sp3gTa zDO-E8Yn@S7u=a?iZ!&&Qf2KKKk7IT`HjO`U*j1~Df9Uxz$~@otSCK;)lbLSmBuIj% zPl&YEoRwsk$8~Az>>djrdtp`PX z`Pu#IITS7lw07vx>YE<4pQ!&Z^7L?{Uox`CJnGjYLh1XN^tt#zY*0}tA*a=V)rf=&-kLgD|;t1D|ORVY}8 F{0H{b<4^zq diff --git a/docs/bridge/static/img/favicon.ico b/docs/bridge/static/img/favicon.ico deleted file mode 100644 index a73399407ac8db158a5224b784e587d1d270a7a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 zcmeI23s6+o8OImU#jZ{P(xG*p9VnlJ(K4Hn3lR=k8uy7FHIsI_nH~ z=5pA3&pF@!`_6a1*SRKBlqts4v!{tR-n6-^$&_F+nd0MpzuTKkpYW_bt$Lr<%4AwW zhab_0A*N(*^<2Lm^ENGiTO`mTfffk_lYopl5hvp-N6X8H9g=czgQV?WF0)+yC2Nm4 z*qBIpE-#$!Dx*(smzR#+1iye^z%Sr;%beYLvS1gMGBPh2a<-!kuPOq+$V-({5C3eJ z$lTqvl3lV=mKDZCA_wyP*=gXf(eaN5|0Lu;)hB;1ev#ag%d%MO6>0co;Q1KvZ;}_P z1pOEE{}%ok`$RIzMCO%>EG!mTQZzf_@IQO54f?Yi{TF!={1c9fOg=B3mnR78R^vwG*;*`kP<06URPX@mY{I7sN6Zy|a{);q`rA2ci4Zp&J?x)G9Q?)Yg zm`KtQktr1-spa6`E7(rK_JF_mf7qebGGkxINaI09oc=DlzZ=_$?E$~Cozr#ODKZEA z+2EgF`iZ2woY>RX*b|*1j)$?wyUT=14cqf|fbF@4{5MGY-ZiX80r;KZU&XroAmVsP zI?{o?o5kDhFj*=2dy{%0kpB$mDWu)N|@{x4|TE+zaPq6q4trrTVjw=7Jl!F?)#E_zVDXrxQX3eiSLVTYEJNH z69d#X8Uq+UG`DyaYZEJ@PMTRKvy9^&f6R=&nOS=?w$qIK%=j-eHq?wBnDGN<*3gVE zHnV@t=)YMq_n6U_*7&@?doRnoK;5?>eM`Ujx4;Qx9n1*TAoVUjx4eehvH@wnqbhA!}6y zeqlYy8+`avh&}v$cTNd$t9D9WEeslW5dT_b*qMJ3!-yWk1j<3hFe(OEq}`O1iub^u z2Yv@M9qbhcF^>cM4)8m$A1ZfrfZqXr2RWDn{0{It!0!OR1N;v5mFum$`U z@LRxdk(2`#nObfEzXkjjY@r4G7VNM^GWJ*`Yqv#Kd=yK}`V@Q4t>+CB{PPB4r(1cC z!oNk97f_)Kelb_JZhQRm50$_A&okR~f;d_ET{IzK+;{Md&~kYyTB%?gsaHbae%MO=q8dfc%~Cxs|?S$Q8p<7r_Q}Bkq2S zz4bSlUVfF>=?nJAA@JM4ACmtv)<-5CiDLgfME=nW{Jp{7i8-Rt(^kl8II`aiekb@p zWbY;;w{IC^ze$TNYK1+GXTSWEecl87&5j?Mq7VLPus_9mzl-dhtjR~{6{%=M-|Mxld zLv;ieQI|Ze*AL&Xs~^^j0|M%AONufh3Or!sey<-6LjJ?fY!0n{SQv5jTcr=^{xtS) zjUIaktsiQa*}uIaO@DO!BTjcDE-J!y2CE;|k~^#<1`bQicQ<4}T+x*{Y&$vRP2w1l zTeind?vqFTHe%0@40+%O;)ugWQBzPk*9LO+<>XMm0DoBGRD(`S+aiG$3EbZj5dFgU zG}(Fvh^T%y;=HXpwtidf`&}L5eQx}A`+mEleB+rz&ExsmGQAZh_R{a-YuC_vpyFC) zGTmU->Mpd*;-YmcamO{%s`orM`rUw<8s}*v;$|MHSq6u+iVf9D7hk)A2kN)W$5$P0 zgCC;}9!~C2a zIHVd>NRv0wiCRxT>O9BEZ)=GKgnH5y;(%W_x0cidnMPkz3vW+6yN7ttTTfT?XSq}@ z?gsQHG&(4Z52X{tO}ogpat>il4__Qx=iYIG{T#YoU=!s*q_fjgt3aSZcS866Ybg&;!(B_fd!Oen+8d$m;w@)$iw(?m!nG4>mSj&(XaB)V8WP z3-S<$s&62!vi>_bKgwq<-U3h0aM21j4|Sb*_TCiEt+Jp$kh>kc4`VI1^8cTx>GYKx z?XIpxAU5bnbN=x(^fz&S^B!lH1Lf6%kn4QZ{iDedpMbYi^g4}w-IFzKR!$}9`=j9f zNycr2{ub)ZGpXOrL{C##o5!e?2bBrubCa-Z7oqRdYu6>_Nt4%hhRlN@6V4D5s9jZ} zr(e@@W>xM%HW$#7p++D7mVMoweN_$p`ul3Zu1m;aB7ysW`lHT09A@6mH~FJAArVd{J;ZU0GX zSKD}(PhB)0`uWh$hkicxH=pyke9n;ab^XsM%SZmy-YYNY=birhRGqWsmfYYwr@{Wb ztN*EyCBvhq!M@IgerDN5=%>JojeTdMmT7~&jdMgB^li|$LEpyu+URRT_ogDp0{y&C z)vMH*t=gyRymMhO^a~r(A70fR8rASuzkl|w|HA*&K)g-;MwZXxBd&60@BLO#eJiET z`xX|T;5)~L^6!2r3e1y`?L|F5_xArMYg^7dTX;7D*#teK4aCD<;{5yAVs!aSpO5gM z%NL;EsQ#((oR>#q)2;MRMd#93$DYXKC(vKdyY0|V!smuu>qZA!qx*f~A)U2Jf&Z>z z-x+lO1@d70hWDR=7OaMSdv+*EbN}Lzah)z zPBA0<@4$O5ILeX9Km6jK>&SLBYuolt=MHrox)9I4K1UlE|ERObiJ{JOr~Qb(5^fX! zY`D{TLmfx_(A?8@t<=LG{Kvfg!T{T@O#i*OIg>lzV zl8|fMH@R!XEuOgQWuDk-lPR`(KuxUr-#tL$x-H8vu77oD{d`67oCbnDv8_l|~W$K$b@3-{XCHsg?n&m@? z4~DWA|4zK%#s?LXe+-fZr9tCmd}kEMtF^|z z)%81t%s+2{waZ!+HaZe1g$QSoFdXZz$E z<*E4bRO07Ua_jxjR&@pSeb(!B<42x)6QA$Rx3l)Rh_43F?@7k@V|+ix_k)N2*hjt@ zAcx2(Q}N!d@neraip^Z7=ZG&KUIFi8iDes{T`=zx$mDItuaVs1I{XR;G@@k9wkT+H zVBIp2{ZugYLVps_pSo{d=%;eAdh#>)Fj{2P4h#4A@J~-u0J^?&DY - - - - - - - - diff --git a/docs/bridge/static/img/undraw_docusaurus_mountain.svg b/docs/bridge/static/img/undraw_docusaurus_mountain.svg deleted file mode 100644 index af961c49a8..0000000000 --- a/docs/bridge/static/img/undraw_docusaurus_mountain.svg +++ /dev/null @@ -1,171 +0,0 @@ - - Easy to Use - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/bridge/static/img/undraw_docusaurus_react.svg b/docs/bridge/static/img/undraw_docusaurus_react.svg deleted file mode 100644 index 94b5cf08f8..0000000000 --- a/docs/bridge/static/img/undraw_docusaurus_react.svg +++ /dev/null @@ -1,170 +0,0 @@ - - Powered by React - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/bridge/static/img/undraw_docusaurus_tree.svg b/docs/bridge/static/img/undraw_docusaurus_tree.svg deleted file mode 100644 index d9161d3392..0000000000 --- a/docs/bridge/static/img/undraw_docusaurus_tree.svg +++ /dev/null @@ -1,40 +0,0 @@ - - Focus on What Matters - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/yarn.lock b/yarn.lock index f4014e15f5..e8daf537c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -348,7 +348,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.14.0", "@babel/core@^7.19.6", "@babel/core@^7.20.12", "@babel/core@^7.20.7", "@babel/core@^7.21.3", "@babel/core@^7.22.9", "@babel/core@^7.23.3", "@babel/core@^7.23.9", "@babel/core@^7.25.2", "@babel/core@^7.4.4", "@babel/core@^7.7.5": +"@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.14.0", "@babel/core@^7.20.12", "@babel/core@^7.20.7", "@babel/core@^7.21.3", "@babel/core@^7.22.9", "@babel/core@^7.23.3", "@babel/core@^7.23.9", "@babel/core@^7.25.2", "@babel/core@^7.4.4", "@babel/core@^7.7.5": version "7.25.2" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.25.2.tgz#ed8eec275118d7613e77a352894cd12ded8eba77" integrity sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA== @@ -594,7 +594,7 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.11.5", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.8", "@babel/parser@^7.20.7", "@babel/parser@^7.22.7", "@babel/parser@^7.23.0", "@babel/parser@^7.23.9", "@babel/parser@^7.25.0", "@babel/parser@^7.25.3", "@babel/parser@^7.25.6", "@babel/parser@^7.7.0": +"@babel/parser@^7.1.0", "@babel/parser@^7.11.5", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.8", "@babel/parser@^7.20.7", "@babel/parser@^7.23.0", "@babel/parser@^7.23.9", "@babel/parser@^7.25.0", "@babel/parser@^7.25.3", "@babel/parser@^7.25.6", "@babel/parser@^7.7.0": version "7.25.6" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.6.tgz#85660c5ef388cbbf6e3d2a694ee97a38f18afe2f" integrity sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q== @@ -1220,7 +1220,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-react-constant-elements@^7.18.12", "@babel/plugin-transform-react-constant-elements@^7.21.3": +"@babel/plugin-transform-react-constant-elements@^7.21.3": version "7.25.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.25.1.tgz#71a665ed16ce618067d05f4a98130207349d82ae" integrity sha512-SLV/giH/V4SmloZ6Dt40HjTGTAIkxn33TVIHxNGNvo8ezMhrxBkzisj4op1KZYPIOHFLqhv60OHvX+YRu4xbmQ== @@ -1365,7 +1365,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.25.2" "@babel/helper-plugin-utils" "^7.24.8" -"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.11", "@babel/preset-env@^7.19.4", "@babel/preset-env@^7.20.2", "@babel/preset-env@^7.22.9", "@babel/preset-env@^7.25.4": +"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.11", "@babel/preset-env@^7.20.2", "@babel/preset-env@^7.22.9", "@babel/preset-env@^7.25.4": version "7.25.4" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.25.4.tgz#be23043d43a34a2721cd0f676c7ba6f1481f6af6" integrity sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw== @@ -1484,7 +1484,7 @@ "@babel/plugin-transform-react-jsx-development" "^7.24.7" "@babel/plugin-transform-react-pure-annotations" "^7.24.7" -"@babel/preset-typescript@^7.12.7", "@babel/preset-typescript@^7.18.6", "@babel/preset-typescript@^7.21.0", "@babel/preset-typescript@^7.22.5", "@babel/preset-typescript@^7.24.7": +"@babel/preset-typescript@^7.12.7", "@babel/preset-typescript@^7.21.0", "@babel/preset-typescript@^7.22.5", "@babel/preset-typescript@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.24.7.tgz#66cd86ea8f8c014855671d5ea9a737139cbbfef1" integrity sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ== @@ -1562,7 +1562,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.2.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.21.3", "@babel/types@^7.24.7", "@babel/types@^7.24.8", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.25.6", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": +"@babel/types@^7.0.0", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.2.0", "@babel/types@^7.20.7", "@babel/types@^7.21.3", "@babel/types@^7.24.7", "@babel/types@^7.24.8", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.25.6", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": version "7.25.6" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.6.tgz#893942ddb858f32ae7a004ec9d3a76b3463ef8e6" integrity sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw== @@ -2038,81 +2038,6 @@ "@docsearch/css" "3.6.1" algoliasearch "^4.19.1" -"@docusaurus/core@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-3.1.1.tgz#29ce8df7a3d3d12ee8962d6d86133b87235ff17b" - integrity sha512-2nQfKFcf+MLEM7JXsXwQxPOmQAR6ytKMZVSx7tVi9HEm9WtfwBH1fp6bn8Gj4zLUhjWKCLoysQ9/Wm+EZCQ4yQ== - dependencies: - "@babel/core" "^7.23.3" - "@babel/generator" "^7.23.3" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-transform-runtime" "^7.22.9" - "@babel/preset-env" "^7.22.9" - "@babel/preset-react" "^7.22.5" - "@babel/preset-typescript" "^7.22.5" - "@babel/runtime" "^7.22.6" - "@babel/runtime-corejs3" "^7.22.6" - "@babel/traverse" "^7.22.8" - "@docusaurus/cssnano-preset" "3.1.1" - "@docusaurus/logger" "3.1.1" - "@docusaurus/mdx-loader" "3.1.1" - "@docusaurus/react-loadable" "5.5.2" - "@docusaurus/utils" "3.1.1" - "@docusaurus/utils-common" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" - "@slorber/static-site-generator-webpack-plugin" "^4.0.7" - "@svgr/webpack" "^6.5.1" - autoprefixer "^10.4.14" - babel-loader "^9.1.3" - babel-plugin-dynamic-import-node "^2.3.3" - boxen "^6.2.1" - chalk "^4.1.2" - chokidar "^3.5.3" - clean-css "^5.3.2" - cli-table3 "^0.6.3" - combine-promises "^1.1.0" - commander "^5.1.0" - copy-webpack-plugin "^11.0.0" - core-js "^3.31.1" - css-loader "^6.8.1" - css-minimizer-webpack-plugin "^4.2.2" - cssnano "^5.1.15" - del "^6.1.1" - detect-port "^1.5.1" - escape-html "^1.0.3" - eta "^2.2.0" - file-loader "^6.2.0" - fs-extra "^11.1.1" - html-minifier-terser "^7.2.0" - html-tags "^3.3.1" - html-webpack-plugin "^5.5.3" - leven "^3.1.0" - lodash "^4.17.21" - mini-css-extract-plugin "^2.7.6" - postcss "^8.4.26" - postcss-loader "^7.3.3" - prompts "^2.4.2" - react-dev-utils "^12.0.1" - react-helmet-async "^1.3.0" - react-loadable "npm:@docusaurus/react-loadable@5.5.2" - react-loadable-ssr-addon-v5-slorber "^1.0.1" - react-router "^5.3.4" - react-router-config "^5.1.1" - react-router-dom "^5.3.4" - rtl-detect "^1.0.4" - semver "^7.5.4" - serve-handler "^6.1.5" - shelljs "^0.8.5" - terser-webpack-plugin "^5.3.9" - tslib "^2.6.0" - update-notifier "^6.0.2" - url-loader "^4.1.1" - webpack "^5.88.1" - webpack-bundle-analyzer "^4.9.0" - webpack-dev-server "^4.15.1" - webpack-merge "^5.9.0" - webpackbar "^5.0.2" - "@docusaurus/core@3.5.2": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-3.5.2.tgz#3adedb90e7b6104592f1231043bd6bf91680c39c" @@ -2187,16 +2112,6 @@ webpack-merge "^5.9.0" webpackbar "^5.0.2" -"@docusaurus/cssnano-preset@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-3.1.1.tgz#03a4cb8e6d41654d7ff5ed79fddd73fd224feea4" - integrity sha512-LnoIDjJWbirdbVZDMq+4hwmrTl2yHDnBf9MLG9qyExeAE3ac35s4yUhJI8yyTCdixzNfKit4cbXblzzqMu4+8g== - dependencies: - cssnano-preset-advanced "^5.3.10" - postcss "^8.4.26" - postcss-sort-media-queries "^4.4.1" - tslib "^2.6.0" - "@docusaurus/cssnano-preset@3.5.2": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-3.5.2.tgz#6c1f2b2f9656f978c4694c84ab24592b04dcfab3" @@ -2207,14 +2122,6 @@ postcss-sort-media-queries "^5.2.0" tslib "^2.6.0" -"@docusaurus/logger@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-3.1.1.tgz#423e8270c00a57b1b3a0cc8a3ee0a4c522a68387" - integrity sha512-BjkNDpQzewcTnST8trx4idSoAla6zZ3w22NqM/UMcFtvYJgmoE4layuTzlfql3VFPNuivvj7BOExa/+21y4X2Q== - dependencies: - chalk "^4.1.2" - tslib "^2.6.0" - "@docusaurus/logger@3.5.2": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-3.5.2.tgz#1150339ad56844b30734115c19c580f3b25cf5ed" @@ -2223,38 +2130,6 @@ chalk "^4.1.2" tslib "^2.6.0" -"@docusaurus/mdx-loader@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-3.1.1.tgz#f79290abc5044bef1d7ecac4eccec887058b8e03" - integrity sha512-xN2IccH9+sv7TmxwsDJNS97BHdmlqWwho+kIVY4tcCXkp+k4QuzvWBeunIMzeayY4Fu13A6sAjHGv5qm72KyGA== - dependencies: - "@babel/parser" "^7.22.7" - "@babel/traverse" "^7.22.8" - "@docusaurus/logger" "3.1.1" - "@docusaurus/utils" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" - "@mdx-js/mdx" "^3.0.0" - "@slorber/remark-comment" "^1.0.0" - escape-html "^1.0.3" - estree-util-value-to-estree "^3.0.1" - file-loader "^6.2.0" - fs-extra "^11.1.1" - image-size "^1.0.2" - mdast-util-mdx "^3.0.0" - mdast-util-to-string "^4.0.0" - rehype-raw "^7.0.0" - remark-directive "^3.0.0" - remark-emoji "^4.0.0" - remark-frontmatter "^5.0.0" - remark-gfm "^4.0.0" - stringify-object "^3.3.0" - tslib "^2.6.0" - unified "^11.0.3" - unist-util-visit "^5.0.0" - url-loader "^4.1.1" - vfile "^6.0.1" - webpack "^5.88.1" - "@docusaurus/mdx-loader@3.5.2": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-3.5.2.tgz#99781641372c5037bcbe09bb8ade93a0e0ada57d" @@ -2285,34 +2160,6 @@ vfile "^6.0.1" webpack "^5.88.1" -"@docusaurus/module-type-aliases@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-3.1.1.tgz#b304402b0535a13ebd4c0db1c368d2604d54d02f" - integrity sha512-xBJyx0TMfAfVZ9ZeIOb1awdXgR4YJMocIEzTps91rq+hJDFJgJaylDtmoRhUxkwuYmNK1GJpW95b7DLztSBJ3A== - dependencies: - "@docusaurus/react-loadable" "5.5.2" - "@docusaurus/types" "3.1.1" - "@types/history" "^4.7.11" - "@types/react" "*" - "@types/react-router-config" "*" - "@types/react-router-dom" "*" - react-helmet-async "*" - react-loadable "npm:@docusaurus/react-loadable@5.5.2" - -"@docusaurus/module-type-aliases@3.2.1": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-3.2.1.tgz#fa8fd746890825b4301db2ddbe29d7cfbeee0380" - integrity sha512-FyViV5TqhL1vsM7eh29nJ5NtbRE6Ra6LP1PDcPvhwPSlA7eiWGRKAn3jWwMUcmjkos5SYY+sr0/feCdbM3eQHQ== - dependencies: - "@docusaurus/react-loadable" "5.5.2" - "@docusaurus/types" "3.2.1" - "@types/history" "^4.7.11" - "@types/react" "*" - "@types/react-router-config" "*" - "@types/react-router-dom" "*" - react-helmet-async "*" - react-loadable "npm:@docusaurus/react-loadable@5.5.2" - "@docusaurus/module-type-aliases@3.5.2": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-3.5.2.tgz#4e8f9c0703e23b2e07ebfce96598ec83e4dd2a9e" @@ -2326,19 +2173,20 @@ react-helmet-async "*" react-loadable "npm:@docusaurus/react-loadable@6.0.0" -"@docusaurus/plugin-content-blog@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.1.1.tgz#16f4fd723227b2158461bba6b9bcc18c1926f7ea" - integrity sha512-ew/3VtVoG3emoAKmoZl7oKe1zdFOsI0NbcHS26kIxt2Z8vcXKCUgK9jJJrz0TbOipyETPhqwq4nbitrY3baibg== - dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/logger" "3.1.1" - "@docusaurus/mdx-loader" "3.1.1" - "@docusaurus/types" "3.1.1" - "@docusaurus/utils" "3.1.1" - "@docusaurus/utils-common" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" - cheerio "^1.0.0-rc.12" +"@docusaurus/plugin-content-blog@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.5.2.tgz#649c07c34da7603645f152bcebdf75285baed16b" + integrity sha512-R7ghWnMvjSf+aeNDH0K4fjyQnt5L0KzUEnUhmf1e3jZrv3wogeytZNN6n7X8yHcMsuZHPOrctQhXWnmxu+IRRg== + dependencies: + "@docusaurus/core" "3.5.2" + "@docusaurus/logger" "3.5.2" + "@docusaurus/mdx-loader" "3.5.2" + "@docusaurus/theme-common" "3.5.2" + "@docusaurus/types" "3.5.2" + "@docusaurus/utils" "3.5.2" + "@docusaurus/utils-common" "3.5.2" + "@docusaurus/utils-validation" "3.5.2" + cheerio "1.0.0-rc.12" feed "^4.2.2" fs-extra "^11.1.1" lodash "^4.17.21" @@ -2349,28 +2197,7 @@ utility-types "^3.10.0" webpack "^5.88.1" -"@docusaurus/plugin-content-docs@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.1.1.tgz#f2eddebf351dd8dd504a2c26061165c519e1f964" - integrity sha512-lhFq4E874zw0UOH7ujzxnCayOyAt0f9YPVYSb9ohxrdCM8B4szxitUw9rIX4V9JLLHVoqIJb6k+lJJ1jrcGJ0A== - dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/logger" "3.1.1" - "@docusaurus/mdx-loader" "3.1.1" - "@docusaurus/module-type-aliases" "3.1.1" - "@docusaurus/types" "3.1.1" - "@docusaurus/utils" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" - "@types/react-router-config" "^5.0.7" - combine-promises "^1.1.0" - fs-extra "^11.1.1" - js-yaml "^4.1.0" - lodash "^4.17.21" - tslib "^2.6.0" - utility-types "^3.10.0" - webpack "^5.88.1" - -"@docusaurus/plugin-content-docs@^3.0.1": +"@docusaurus/plugin-content-docs@3.5.2", "@docusaurus/plugin-content-docs@^3.0.1": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.5.2.tgz#adcf6c0bd9a9818eb192ab831e0069ee62d31505" integrity sha512-Bt+OXn/CPtVqM3Di44vHjE7rPCEsRCB/DMo2qoOuozB9f7+lsdrHvD0QCHdBs0uhz6deYJDppAr2VgqybKPlVQ== @@ -2393,126 +2220,118 @@ utility-types "^3.10.0" webpack "^5.88.1" -"@docusaurus/plugin-content-pages@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.1.1.tgz#05aec68c2abeac2140c7a16d4c5b506bf4d19fb2" - integrity sha512-NQHncNRAJbyLtgTim9GlEnNYsFhuCxaCNkMwikuxLTiGIPH7r/jpb7O3f3jUMYMebZZZrDq5S7om9a6rvB/YCA== - dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/mdx-loader" "3.1.1" - "@docusaurus/types" "3.1.1" - "@docusaurus/utils" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" +"@docusaurus/plugin-content-pages@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.5.2.tgz#2b59e43f5bc5b5176ff01835de706f1c65c2e68b" + integrity sha512-WzhHjNpoQAUz/ueO10cnundRz+VUtkjFhhaQ9jApyv1a46FPURO4cef89pyNIOMny1fjDz/NUN2z6Yi+5WUrCw== + dependencies: + "@docusaurus/core" "3.5.2" + "@docusaurus/mdx-loader" "3.5.2" + "@docusaurus/types" "3.5.2" + "@docusaurus/utils" "3.5.2" + "@docusaurus/utils-validation" "3.5.2" fs-extra "^11.1.1" tslib "^2.6.0" webpack "^5.88.1" -"@docusaurus/plugin-debug@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-3.1.1.tgz#cee5aae1fef288fb93f68894db79a2612e313d3f" - integrity sha512-xWeMkueM9wE/8LVvl4+Qf1WqwXmreMjI5Kgr7GYCDoJ8zu4kD+KaMhrh7py7MNM38IFvU1RfrGKacCEe2DRRfQ== +"@docusaurus/plugin-debug@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-3.5.2.tgz#c25ca6a59e62a17c797b367173fe80c06fdf2f65" + integrity sha512-kBK6GlN0itCkrmHuCS6aX1wmoWc5wpd5KJlqQ1FyrF0cLDnvsYSnh7+ftdwzt7G6lGBho8lrVwkkL9/iQvaSOA== dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/types" "3.1.1" - "@docusaurus/utils" "3.1.1" + "@docusaurus/core" "3.5.2" + "@docusaurus/types" "3.5.2" + "@docusaurus/utils" "3.5.2" fs-extra "^11.1.1" react-json-view-lite "^1.2.0" tslib "^2.6.0" -"@docusaurus/plugin-google-analytics@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.1.1.tgz#bfc58205b4fcaf3222e04f9c3542f3bef9804887" - integrity sha512-+q2UpWTqVi8GdlLoSlD5bS/YpxW+QMoBwrPrUH/NpvpuOi0Of7MTotsQf9JWd3hymZxl2uu1o3PIrbpxfeDFDQ== +"@docusaurus/plugin-google-analytics@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.5.2.tgz#1143e78d1461d3c74a2746f036d25b18d4a2608d" + integrity sha512-rjEkJH/tJ8OXRE9bwhV2mb/WP93V441rD6XnM6MIluu7rk8qg38iSxS43ga2V2Q/2ib53PcqbDEJDG/yWQRJhQ== dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/types" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" + "@docusaurus/core" "3.5.2" + "@docusaurus/types" "3.5.2" + "@docusaurus/utils-validation" "3.5.2" tslib "^2.6.0" -"@docusaurus/plugin-google-gtag@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.1.1.tgz#7e8b5aa6847a12461c104a65a335f4a45dae2f28" - integrity sha512-0mMPiBBlQ5LFHTtjxuvt/6yzh8v7OxLi3CbeEsxXZpUzcKO/GC7UA1VOWUoBeQzQL508J12HTAlR3IBU9OofSw== +"@docusaurus/plugin-google-gtag@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.5.2.tgz#60b5a9e1888c4fa16933f7c5cb5f2f2c31caad3a" + integrity sha512-lm8XL3xLkTPHFKKjLjEEAHUrW0SZBSHBE1I+i/tmYMBsjCcUB5UJ52geS5PSiOCFVR74tbPGcPHEV/gaaxFeSA== dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/types" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" + "@docusaurus/core" "3.5.2" + "@docusaurus/types" "3.5.2" + "@docusaurus/utils-validation" "3.5.2" "@types/gtag.js" "^0.0.12" tslib "^2.6.0" -"@docusaurus/plugin-google-tag-manager@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.1.1.tgz#e1aae4d821e786d133386b4ae6e6fe66a4bc0089" - integrity sha512-d07bsrMLdDIryDtY17DgqYUbjkswZQr8cLWl4tzXrt5OR/T/zxC1SYKajzB3fd87zTu5W5klV5GmUwcNSMXQXA== +"@docusaurus/plugin-google-tag-manager@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.5.2.tgz#7a37334d2e7f00914d61ad05bc09391c4db3bfda" + integrity sha512-QkpX68PMOMu10Mvgvr5CfZAzZQFx8WLlOiUQ/Qmmcl6mjGK6H21WLT5x7xDmcpCoKA/3CegsqIqBR+nA137lQg== dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/types" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" + "@docusaurus/core" "3.5.2" + "@docusaurus/types" "3.5.2" + "@docusaurus/utils-validation" "3.5.2" tslib "^2.6.0" -"@docusaurus/plugin-sitemap@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.1.1.tgz#8828bf5e2922273aad207a35189f22913e6a0dfd" - integrity sha512-iJ4hCaMmDaUqRv131XJdt/C/jJQx8UreDWTRqZKtNydvZVh/o4yXGRRFOplea1D9b/zpwL1Y+ZDwX7xMhIOTmg== - dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/logger" "3.1.1" - "@docusaurus/types" "3.1.1" - "@docusaurus/utils" "3.1.1" - "@docusaurus/utils-common" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" +"@docusaurus/plugin-sitemap@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.5.2.tgz#9c940b27f3461c54d65295cf4c52cb20538bd360" + integrity sha512-DnlqYyRAdQ4NHY28TfHuVk414ft2uruP4QWCH//jzpHjqvKyXjj2fmDtI8RPUBh9K8iZKFMHRnLtzJKySPWvFA== + dependencies: + "@docusaurus/core" "3.5.2" + "@docusaurus/logger" "3.5.2" + "@docusaurus/types" "3.5.2" + "@docusaurus/utils" "3.5.2" + "@docusaurus/utils-common" "3.5.2" + "@docusaurus/utils-validation" "3.5.2" fs-extra "^11.1.1" sitemap "^7.1.1" tslib "^2.6.0" -"@docusaurus/preset-classic@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-3.1.1.tgz#15fd80012529dafd7e01cc0bce59d39ee6ad6bf5" - integrity sha512-jG4ys/hWYf69iaN/xOmF+3kjs4Nnz1Ay3CjFLDtYa8KdxbmUhArA9HmP26ru5N0wbVWhY+6kmpYhTJpez5wTyg== - dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/plugin-content-blog" "3.1.1" - "@docusaurus/plugin-content-docs" "3.1.1" - "@docusaurus/plugin-content-pages" "3.1.1" - "@docusaurus/plugin-debug" "3.1.1" - "@docusaurus/plugin-google-analytics" "3.1.1" - "@docusaurus/plugin-google-gtag" "3.1.1" - "@docusaurus/plugin-google-tag-manager" "3.1.1" - "@docusaurus/plugin-sitemap" "3.1.1" - "@docusaurus/theme-classic" "3.1.1" - "@docusaurus/theme-common" "3.1.1" - "@docusaurus/theme-search-algolia" "3.1.1" - "@docusaurus/types" "3.1.1" - -"@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2": - version "5.5.2" - resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce" - integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ== +"@docusaurus/preset-classic@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-3.5.2.tgz#977f78510bbc556aa0539149eef960bb7ab52bd9" + integrity sha512-3ihfXQ95aOHiLB5uCu+9PRy2gZCeSZoDcqpnDvf3B+sTrMvMTr8qRUzBvWkoIqc82yG5prCboRjk1SVILKx6sg== dependencies: - "@types/react" "*" - prop-types "^15.6.2" + "@docusaurus/core" "3.5.2" + "@docusaurus/plugin-content-blog" "3.5.2" + "@docusaurus/plugin-content-docs" "3.5.2" + "@docusaurus/plugin-content-pages" "3.5.2" + "@docusaurus/plugin-debug" "3.5.2" + "@docusaurus/plugin-google-analytics" "3.5.2" + "@docusaurus/plugin-google-gtag" "3.5.2" + "@docusaurus/plugin-google-tag-manager" "3.5.2" + "@docusaurus/plugin-sitemap" "3.5.2" + "@docusaurus/theme-classic" "3.5.2" + "@docusaurus/theme-common" "3.5.2" + "@docusaurus/theme-search-algolia" "3.5.2" + "@docusaurus/types" "3.5.2" -"@docusaurus/theme-classic@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-3.1.1.tgz#0a188c787fc4bf2bb525cc30c7aa34e555ee96b8" - integrity sha512-GiPE/jbWM8Qv1A14lk6s9fhc0LhPEQ00eIczRO4QL2nAQJZXkjPG6zaVx+1cZxPFWbAsqSjKe2lqkwF3fGkQ7Q== - dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/mdx-loader" "3.1.1" - "@docusaurus/module-type-aliases" "3.1.1" - "@docusaurus/plugin-content-blog" "3.1.1" - "@docusaurus/plugin-content-docs" "3.1.1" - "@docusaurus/plugin-content-pages" "3.1.1" - "@docusaurus/theme-common" "3.1.1" - "@docusaurus/theme-translations" "3.1.1" - "@docusaurus/types" "3.1.1" - "@docusaurus/utils" "3.1.1" - "@docusaurus/utils-common" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" +"@docusaurus/theme-classic@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-3.5.2.tgz#602ddb63d987ab1f939e3760c67bc1880f01c000" + integrity sha512-XRpinSix3NBv95Rk7xeMF9k4safMkwnpSgThn0UNQNumKvmcIYjfkwfh2BhwYh/BxMXQHJ/PdmNh22TQFpIaYg== + dependencies: + "@docusaurus/core" "3.5.2" + "@docusaurus/mdx-loader" "3.5.2" + "@docusaurus/module-type-aliases" "3.5.2" + "@docusaurus/plugin-content-blog" "3.5.2" + "@docusaurus/plugin-content-docs" "3.5.2" + "@docusaurus/plugin-content-pages" "3.5.2" + "@docusaurus/theme-common" "3.5.2" + "@docusaurus/theme-translations" "3.5.2" + "@docusaurus/types" "3.5.2" + "@docusaurus/utils" "3.5.2" + "@docusaurus/utils-common" "3.5.2" + "@docusaurus/utils-validation" "3.5.2" "@mdx-js/react" "^3.0.0" clsx "^2.0.0" copy-text-to-clipboard "^3.2.0" - infima "0.2.0-alpha.43" + infima "0.2.0-alpha.44" lodash "^4.17.21" nprogress "^0.2.0" postcss "^8.4.26" @@ -2523,27 +2342,6 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@docusaurus/theme-common@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-3.1.1.tgz#5a16893928b8379c9e83aef01d753e7e142459e2" - integrity sha512-38urZfeMhN70YaXkwIGXmcUcv2CEYK/2l4b05GkJPrbEbgpsIZM3Xc+Js2ehBGGZmfZq8GjjQ5RNQYG+MYzCYg== - dependencies: - "@docusaurus/mdx-loader" "3.1.1" - "@docusaurus/module-type-aliases" "3.1.1" - "@docusaurus/plugin-content-blog" "3.1.1" - "@docusaurus/plugin-content-docs" "3.1.1" - "@docusaurus/plugin-content-pages" "3.1.1" - "@docusaurus/utils" "3.1.1" - "@docusaurus/utils-common" "3.1.1" - "@types/history" "^4.7.11" - "@types/react" "*" - "@types/react-router-config" "*" - clsx "^2.0.0" - parse-numeric-range "^1.3.0" - prism-react-renderer "^2.3.0" - tslib "^2.6.0" - utility-types "^3.10.0" - "@docusaurus/theme-common@3.5.2", "@docusaurus/theme-common@^3.0.1": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-3.5.2.tgz#b507ab869a1fba0be9c3c9d74f2f3d74c3ac78b2" @@ -2562,19 +2360,19 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@docusaurus/theme-search-algolia@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.1.1.tgz#5170cd68cc59d150416b070bdc6d15c363ddf5e1" - integrity sha512-tBH9VY5EpRctVdaAhT+b1BY8y5dyHVZGFXyCHgTrvcXQy5CV4q7serEX7U3SveNT9zksmchPyct6i1sFDC4Z5g== +"@docusaurus/theme-search-algolia@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.5.2.tgz#466c83ca7e8017d95ae6889ccddc5ef8bf6b61c6" + integrity sha512-qW53kp3VzMnEqZGjakaV90sst3iN1o32PH+nawv1uepROO8aEGxptcq2R5rsv7aBShSRbZwIobdvSYKsZ5pqvA== dependencies: "@docsearch/react" "^3.5.2" - "@docusaurus/core" "3.1.1" - "@docusaurus/logger" "3.1.1" - "@docusaurus/plugin-content-docs" "3.1.1" - "@docusaurus/theme-common" "3.1.1" - "@docusaurus/theme-translations" "3.1.1" - "@docusaurus/utils" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" + "@docusaurus/core" "3.5.2" + "@docusaurus/logger" "3.5.2" + "@docusaurus/plugin-content-docs" "3.5.2" + "@docusaurus/theme-common" "3.5.2" + "@docusaurus/theme-translations" "3.5.2" + "@docusaurus/utils" "3.5.2" + "@docusaurus/utils-validation" "3.5.2" algoliasearch "^4.18.0" algoliasearch-helper "^3.13.3" clsx "^2.0.0" @@ -2584,48 +2382,18 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@docusaurus/theme-translations@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-3.1.1.tgz#117e91ba5e3a8178cb59f3028bf41de165a508c1" - integrity sha512-xvWQFwjxHphpJq5fgk37FXCDdAa2o+r7FX8IpMg+bGZBNXyWBu3MjZ+G4+eUVNpDhVinTc+j6ucL0Ain5KCGrg== +"@docusaurus/theme-translations@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-3.5.2.tgz#38f9ebf2a5d860397022206a05fef66c08863c89" + integrity sha512-GPZLcu4aT1EmqSTmbdpVrDENGR2yObFEX8ssEFYTCiAIVc0EihNSdOIBTazUvgNqwvnoU1A8vIs1xyzc3LITTw== dependencies: fs-extra "^11.1.1" tslib "^2.6.0" -"@docusaurus/tsconfig@3.2.1": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@docusaurus/tsconfig/-/tsconfig-3.2.1.tgz#6bdc0cb46414d09c7334d632b6d5e5472e6eb5a7" - integrity sha512-+biUwtsYW3oChLxYezzA+NIgS3Q9KDRl7add/YT54RXs9Q4rKInebxdHdG6JFs5BaTg45gyjDu0rvNVcGeHODg== - -"@docusaurus/types@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-3.1.1.tgz#747c9dee8cf7c3b0e5ee7351bac5e9c4fdc7f259" - integrity sha512-grBqOLnubUecgKFXN9q3uit2HFbCxTWX4Fam3ZFbMN0sWX9wOcDoA7lwdX/8AmeL20Oc4kQvWVgNrsT8bKRvzg== - dependencies: - "@mdx-js/mdx" "^3.0.0" - "@types/history" "^4.7.11" - "@types/react" "*" - commander "^5.1.0" - joi "^17.9.2" - react-helmet-async "^1.3.0" - utility-types "^3.10.0" - webpack "^5.88.1" - webpack-merge "^5.9.0" - -"@docusaurus/types@3.2.1": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-3.2.1.tgz#88ccd4b8fa236628a29c89b8b0f60f0cc4716b69" - integrity sha512-n/toxBzL2oxTtRTOFiGKsHypzn/Pm+sXyw+VSk1UbqbXQiHOwHwts55bpKwbcUgA530Is6kix3ELiFOv9GAMfw== - dependencies: - "@mdx-js/mdx" "^3.0.0" - "@types/history" "^4.7.11" - "@types/react" "*" - commander "^5.1.0" - joi "^17.9.2" - react-helmet-async "^1.3.0" - utility-types "^3.10.0" - webpack "^5.88.1" - webpack-merge "^5.9.0" +"@docusaurus/tsconfig@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/tsconfig/-/tsconfig-3.5.2.tgz#98878103ba217bff355cd8944926d9ca06e6e153" + integrity sha512-rQ7toURCFnWAIn8ubcquDs0ewhPwviMzxh6WpRjBW7sJVCXb6yzwUaY3HMNa0VXCFw+qkIbFywrMTf+Pb4uHWQ== "@docusaurus/types@3.5.2": version "3.5.2" @@ -2642,13 +2410,6 @@ webpack "^5.88.1" webpack-merge "^5.9.0" -"@docusaurus/utils-common@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-3.1.1.tgz#b48fade63523fd40f3adb67b47c3371e5183c20b" - integrity sha512-eGne3olsIoNfPug5ixjepZAIxeYFzHHnor55Wb2P57jNbtVaFvij/T+MS8U0dtZRFi50QU+UPmRrXdVUM8uyMg== - dependencies: - tslib "^2.6.0" - "@docusaurus/utils-common@3.5.2": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-3.5.2.tgz#4d7f5e962fbca3e2239d80457aa0e4bd3d8f7e0a" @@ -2656,17 +2417,6 @@ dependencies: tslib "^2.6.0" -"@docusaurus/utils-validation@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-3.1.1.tgz#3a747349ed05aee0e4d543552b41f3c9467ee731" - integrity sha512-KlY4P9YVDnwL+nExvlIpu79abfEv6ZCHuOX4ZQ+gtip+Wxj0daccdReIWWtqxM/Fb5Cz1nQvUCc7VEtT8IBUAA== - dependencies: - "@docusaurus/logger" "3.1.1" - "@docusaurus/utils" "3.1.1" - joi "^17.9.2" - js-yaml "^4.1.0" - tslib "^2.6.0" - "@docusaurus/utils-validation@3.5.2", "@docusaurus/utils-validation@^3.0.1": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-3.5.2.tgz#1b2b2f02082781cc8ce713d4c85e88d6d2fc4eb3" @@ -2681,29 +2431,6 @@ lodash "^4.17.21" tslib "^2.6.0" -"@docusaurus/utils@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-3.1.1.tgz#e822d14704e4b3bb451ca464a7cc56aea9b55a45" - integrity sha512-ZJfJa5cJQtRYtqijsPEnAZoduW6sjAQ7ZCWSZavLcV10Fw0Z3gSaPKA/B4micvj2afRZ4gZxT7KfYqe5H8Cetg== - dependencies: - "@docusaurus/logger" "3.1.1" - "@svgr/webpack" "^6.5.1" - escape-string-regexp "^4.0.0" - file-loader "^6.2.0" - fs-extra "^11.1.1" - github-slugger "^1.5.0" - globby "^11.1.0" - gray-matter "^4.0.3" - jiti "^1.20.0" - js-yaml "^4.1.0" - lodash "^4.17.21" - micromatch "^4.0.5" - resolve-pathname "^3.0.0" - shelljs "^0.8.5" - tslib "^2.6.0" - url-loader "^4.1.1" - webpack "^5.88.1" - "@docusaurus/utils@3.5.2", "@docusaurus/utils@^3.0.1": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-3.5.2.tgz#17763130215f18d7269025903588ef7fb373e2cb" @@ -6668,49 +6395,6 @@ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.0.1.tgz#93da90fc209a0a4ff09c1deb037fbb35e4020890" integrity sha512-yQJaT5HDp9hYOOp4jTYxMsR02gdFZFXhewX5HW9Jo4fsqSVqqyIO/xTHdWDaKX5a3pv1txmf076Lziz+sO7L1w== -"@paloaltonetworks/openapi-to-postmanv2@3.1.0-hotfix.1": - version "3.1.0-hotfix.1" - resolved "https://registry.yarnpkg.com/@paloaltonetworks/openapi-to-postmanv2/-/openapi-to-postmanv2-3.1.0-hotfix.1.tgz#4baf401d2e94ba86d888e6011a4c45d824e88114" - integrity sha512-0bdaPCEyQbnUo4xpOu7EzxXXkDx4BAXqc8QSbVBlzlVB5KoTLJiKKB4c3fa4BXbK+3u/OqfLbeNCebc2EC8ngA== - dependencies: - "@paloaltonetworks/postman-collection" "^4.1.0" - ajv "8.1.0" - ajv-formats "2.1.1" - async "3.2.1" - commander "2.20.3" - js-yaml "3.14.1" - json-schema-merge-allof "0.8.1" - lodash "4.17.21" - oas-resolver-browser "2.5.2" - path-browserify "1.0.1" - yaml "1.10.2" - -"@paloaltonetworks/postman-code-generators@1.1.15-patch.2": - version "1.1.15-patch.2" - resolved "https://registry.yarnpkg.com/@paloaltonetworks/postman-code-generators/-/postman-code-generators-1.1.15-patch.2.tgz#012051485269a2da6bd9a6b60031ddbc53e5e363" - integrity sha512-tRnAKtV4M8wLxcVnAx6ZCjCqbrR1xiqJNQkf1A71K8UxEP3N/+EspT82N5c0555w02oYFk21ViHuzuhm4gaGLw== - dependencies: - "@paloaltonetworks/postman-collection" "^4.1.0" - async "^3.2.4" - path "^0.12.7" - shelljs "^0.8.5" - -"@paloaltonetworks/postman-collection@^4.1.0": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@paloaltonetworks/postman-collection/-/postman-collection-4.1.1.tgz#b2130bc8d7396ea8e6a6b2e4642a6b224b41e1e1" - integrity sha512-9JHHkkD8Xb4rvdKob7TDPRfqfmdG3KU0aO5gJyyjvMFbOVysam5I0d8/9HPOuJXWkUHGo3Sn+ov2Fcm2bnJ52Q== - dependencies: - file-type "3.9.0" - http-reasons "0.1.0" - iconv-lite "0.6.3" - liquid-json "0.3.1" - lodash "4.17.21" - mime-format "2.0.1" - mime-types "2.1.34" - postman-url-encoder "3.0.5" - semver "7.3.5" - uuid "8.3.2" - "@parcel/watcher-android-arm64@2.4.1": version "2.4.1" resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz#c2c19a3c442313ff007d2d7a9c2c1dd3e1c9ca84" @@ -7056,19 +6740,7 @@ dependencies: "@rollup/pluginutils" "^5.1.0" -"@rollup/plugin-node-resolve@^15.2.3": - version "15.2.3" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz#e5e0b059bd85ca57489492f295ce88c2d4b0daf9" - integrity sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ== - dependencies: - "@rollup/pluginutils" "^5.0.1" - "@types/resolve" "1.20.2" - deepmerge "^4.2.2" - is-builtin-module "^3.2.1" - is-module "^1.0.0" - resolve "^1.22.1" - -"@rollup/plugin-node-resolve@^15.3.0": +"@rollup/plugin-node-resolve@^15.2.3", "@rollup/plugin-node-resolve@^15.3.0": version "15.3.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz#efbb35515c9672e541c08d59caba2eff492a55d5" integrity sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag== @@ -7143,9 +6815,9 @@ picomatch "^2.2.2" "@rollup/pluginutils@^5.0.1", "@rollup/pluginutils@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.0.tgz#7e53eddc8c7f483a4ad0b94afb1f7f5fd3c771e0" - integrity sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g== + version "5.1.2" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.2.tgz#d3bc9f0fea4fd4086aaac6aa102f3fa587ce8bd9" + integrity sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw== dependencies: "@types/estree" "^1.0.0" estree-walker "^2.0.2" @@ -7615,15 +7287,6 @@ micromark-util-character "^1.1.0" micromark-util-symbol "^1.0.1" -"@slorber/static-site-generator-webpack-plugin@^4.0.7": - version "4.0.7" - resolved "https://registry.yarnpkg.com/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz#fc1678bddefab014e2145cbe25b3ce4e1cfc36f3" - integrity sha512-Ug7x6z5lwrz0WqdnNFOMYrDQNTPAprvHLSh6+/fmml3qUiz6l5eq+2MzLKWtn/q5K5NpSiFsZTP/fck/3vjSxA== - dependencies: - eval "^0.1.8" - p-map "^4.0.0" - webpack-sources "^3.2.2" - "@socket.io/component-emitter@~3.1.0": version "3.1.2" resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2" @@ -8718,17 +8381,12 @@ resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz#4001f5d5dd87fa13303e36ee106e3ff3a7eb8b22" integrity sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g== -"@svgr/babel-plugin-add-jsx-attribute@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz#74a5d648bd0347bda99d82409d87b8ca80b9a1ba" - integrity sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ== - -"@svgr/babel-plugin-remove-jsx-attribute@*", "@svgr/babel-plugin-remove-jsx-attribute@8.0.0": +"@svgr/babel-plugin-remove-jsx-attribute@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz#69177f7937233caca3a1afb051906698f2f59186" integrity sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA== -"@svgr/babel-plugin-remove-jsx-empty-expression@*", "@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0": +"@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz#c2c48104cfd7dcd557f373b70a56e9e3bdae1d44" integrity sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA== @@ -8738,51 +8396,26 @@ resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz#8fbb6b2e91fa26ac5d4aa25c6b6e4f20f9c0ae27" integrity sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ== -"@svgr/babel-plugin-replace-jsx-attribute-value@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz#fb9d22ea26d2bc5e0a44b763d4c46d5d3f596c60" - integrity sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg== - "@svgr/babel-plugin-svg-dynamic-title@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz#1d5ba1d281363fc0f2f29a60d6d936f9bbc657b0" integrity sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og== -"@svgr/babel-plugin-svg-dynamic-title@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz#01b2024a2b53ffaa5efceaa0bf3e1d5a4c520ce4" - integrity sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw== - "@svgr/babel-plugin-svg-em-dimensions@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz#35e08df300ea8b1d41cb8f62309c241b0369e501" integrity sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g== -"@svgr/babel-plugin-svg-em-dimensions@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz#dd3fa9f5b24eb4f93bcf121c3d40ff5facecb217" - integrity sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA== - "@svgr/babel-plugin-transform-react-native-svg@8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz#90a8b63998b688b284f255c6a5248abd5b28d754" integrity sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q== -"@svgr/babel-plugin-transform-react-native-svg@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz#1d8e945a03df65b601551097d8f5e34351d3d305" - integrity sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg== - "@svgr/babel-plugin-transform-svg-component@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz#013b4bfca88779711f0ed2739f3f7efcefcf4f7e" integrity sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw== -"@svgr/babel-plugin-transform-svg-component@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz#48620b9e590e25ff95a80f811544218d27f8a250" - integrity sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ== - "@svgr/babel-preset@8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-8.1.0.tgz#0e87119aecdf1c424840b9d4565b7137cabf9ece" @@ -8797,20 +8430,6 @@ "@svgr/babel-plugin-transform-react-native-svg" "8.1.0" "@svgr/babel-plugin-transform-svg-component" "8.0.0" -"@svgr/babel-preset@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-6.5.1.tgz#b90de7979c8843c5c580c7e2ec71f024b49eb828" - integrity sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw== - dependencies: - "@svgr/babel-plugin-add-jsx-attribute" "^6.5.1" - "@svgr/babel-plugin-remove-jsx-attribute" "*" - "@svgr/babel-plugin-remove-jsx-empty-expression" "*" - "@svgr/babel-plugin-replace-jsx-attribute-value" "^6.5.1" - "@svgr/babel-plugin-svg-dynamic-title" "^6.5.1" - "@svgr/babel-plugin-svg-em-dimensions" "^6.5.1" - "@svgr/babel-plugin-transform-react-native-svg" "^6.5.1" - "@svgr/babel-plugin-transform-svg-component" "^6.5.1" - "@svgr/core@8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@svgr/core/-/core-8.1.0.tgz#41146f9b40b1a10beaf5cc4f361a16a3c1885e88" @@ -8822,17 +8441,6 @@ cosmiconfig "^8.1.3" snake-case "^3.0.4" -"@svgr/core@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/core/-/core-6.5.1.tgz#d3e8aa9dbe3fbd747f9ee4282c1c77a27410488a" - integrity sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw== - dependencies: - "@babel/core" "^7.19.6" - "@svgr/babel-preset" "^6.5.1" - "@svgr/plugin-jsx" "^6.5.1" - camelcase "^6.2.0" - cosmiconfig "^7.0.1" - "@svgr/hast-util-to-babel-ast@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz#6952fd9ce0f470e1aded293b792a2705faf4ffd4" @@ -8841,14 +8449,6 @@ "@babel/types" "^7.21.3" entities "^4.4.0" -"@svgr/hast-util-to-babel-ast@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz#81800bd09b5bcdb968bf6ee7c863d2288fdb80d2" - integrity sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw== - dependencies: - "@babel/types" "^7.20.0" - entities "^4.4.0" - "@svgr/plugin-jsx@8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz#96969f04a24b58b174ee4cd974c60475acbd6928" @@ -8859,16 +8459,6 @@ "@svgr/hast-util-to-babel-ast" "8.0.0" svg-parser "^2.0.4" -"@svgr/plugin-jsx@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz#0e30d1878e771ca753c94e69581c7971542a7072" - integrity sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw== - dependencies: - "@babel/core" "^7.19.6" - "@svgr/babel-preset" "^6.5.1" - "@svgr/hast-util-to-babel-ast" "^6.5.1" - svg-parser "^2.0.4" - "@svgr/plugin-svgo@8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz#b115b7b967b564f89ac58feae89b88c3decd0f00" @@ -8878,29 +8468,6 @@ deepmerge "^4.3.1" svgo "^3.0.2" -"@svgr/plugin-svgo@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz#0f91910e988fc0b842f88e0960c2862e022abe84" - integrity sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ== - dependencies: - cosmiconfig "^7.0.1" - deepmerge "^4.2.2" - svgo "^2.8.0" - -"@svgr/webpack@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-6.5.1.tgz#ecf027814fc1cb2decc29dc92f39c3cf691e40e8" - integrity sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA== - dependencies: - "@babel/core" "^7.19.6" - "@babel/plugin-transform-react-constant-elements" "^7.18.12" - "@babel/preset-env" "^7.19.4" - "@babel/preset-react" "^7.18.6" - "@babel/preset-typescript" "^7.18.6" - "@svgr/core" "^6.5.1" - "@svgr/plugin-jsx" "^6.5.1" - "@svgr/plugin-svgo" "^6.5.1" - "@svgr/webpack@^8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-8.1.0.tgz#16f1b5346f102f89fda6ec7338b96a701d8be0c2" @@ -9860,9 +9427,9 @@ "@types/react" "*" "@types/react-redux@^7.1.20": - version "7.1.33" - resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.33.tgz#53c5564f03f1ded90904e3c90f77e4bd4dc20b15" - integrity sha512-NF8m5AjWCkert+fosDsN3hAlHzpjSiXlVy9EgQEmLoBhaNXbmyeGs/aj5dQzKuF+/q+S7JQagorGDW8pJ28Hmg== + version "7.1.34" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.34.tgz#83613e1957c481521e6776beeac4fd506d11bd0e" + integrity sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ== dependencies: "@types/hoist-non-react-statics" "^3.3.0" "@types/react" "*" @@ -11571,16 +11138,6 @@ ajv-keywords@^5.1.0: dependencies: fast-deep-equal "^3.1.3" -ajv@8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.1.0.tgz#45d5d3d36c7cdd808930cc3e603cf6200dbeb736" - integrity sha512-B/Sk2Ix7A36fs/ZkuGLIR86EdjbgR6fsAcbx9lOP/QBSXujDNbVmIS/U4Itz5k8fPFDeVZl/zQ/gJW4Jrq6XjQ== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - ajv@8.11.0: version "8.11.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" @@ -12235,17 +11792,17 @@ async@1.x: resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" integrity sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w== -async@3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.1.tgz#d3274ec66d107a47476a4c49136aacdb00665fc8" - integrity sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg== +async@3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.2.tgz#2eb7671034bb2194d45d30e31e24ec7e7f9670cd" + integrity sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g== async@3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== -async@^3.2.3, async@^3.2.4: +async@^3.2.3: version "3.2.6" resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== @@ -12293,7 +11850,7 @@ auto-bind@~4.0.0: resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" integrity sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ== -autoprefixer@^10.4.12, autoprefixer@^10.4.14, autoprefixer@^10.4.16, autoprefixer@^10.4.19, autoprefixer@^10.4.7: +autoprefixer@^10.4.14, autoprefixer@^10.4.16, autoprefixer@^10.4.19, autoprefixer@^10.4.7: version "10.4.20" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.20.tgz#5caec14d43976ef42e32dcb4bd62878e96be5b3b" integrity sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g== @@ -13704,22 +13261,18 @@ cheerio-select@^2.1.0: domhandler "^5.0.3" domutils "^3.0.1" -cheerio@^1.0.0-rc.12: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0.tgz#1ede4895a82f26e8af71009f961a9b8cb60d6a81" - integrity sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww== +cheerio@1.0.0-rc.12: + version "1.0.0-rc.12" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.12.tgz#788bf7466506b1c6bf5fae51d24a2c4d62e47683" + integrity sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q== dependencies: cheerio-select "^2.1.0" dom-serializer "^2.0.0" domhandler "^5.0.3" - domutils "^3.1.0" - encoding-sniffer "^0.2.0" - htmlparser2 "^9.1.0" - parse5 "^7.1.2" + domutils "^3.0.1" + htmlparser2 "^8.0.1" + parse5 "^7.0.0" parse5-htmlparser2-tree-adapter "^7.0.0" - parse5-parser-stream "^7.1.2" - undici "^6.19.5" - whatwg-mimetype "^4.0.0" chokidar@3.3.1: version "3.3.1" @@ -14692,7 +14245,7 @@ copy-webpack-plugin@^11.0.0: schema-utils "^4.0.0" serialize-javascript "^6.0.0" -copyfiles@^2.3.0: +copyfiles@^2.3.0, copyfiles@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/copyfiles/-/copyfiles-2.4.1.tgz#d2dcff60aaad1015f09d0b66e7f0f1c5cd3c5da5" integrity sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg== @@ -14756,7 +14309,7 @@ cosmiconfig@^6.0.0: path-type "^4.0.0" yaml "^1.7.2" -cosmiconfig@^7.0.0, cosmiconfig@^7.0.1, cosmiconfig@^7.1.0: +cosmiconfig@^7.0.0, cosmiconfig@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== @@ -15009,18 +14562,6 @@ css-loader@^6.8.1: postcss-value-parser "^4.2.0" semver "^7.5.4" -css-minimizer-webpack-plugin@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz#79f6199eb5adf1ff7ba57f105e3752d15211eb35" - integrity sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA== - dependencies: - cssnano "^5.1.8" - jest-worker "^29.1.2" - postcss "^8.4.17" - schema-utils "^4.0.0" - serialize-javascript "^6.0.0" - source-map "^0.6.1" - css-minimizer-webpack-plugin@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz#33effe662edb1a0bf08ad633c32fa75d0f7ec565" @@ -15094,18 +14635,6 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -cssnano-preset-advanced@^5.3.10: - version "5.3.10" - resolved "https://registry.yarnpkg.com/cssnano-preset-advanced/-/cssnano-preset-advanced-5.3.10.tgz#25558a1fbf3a871fb6429ce71e41be7f5aca6eef" - integrity sha512-fnYJyCS9jgMU+cmHO1rPSPf9axbQyD7iUhLO5Df6O4G+fKIOMps+ZbU0PdGFejFBBZ3Pftf18fn1eG7MAPUSWQ== - dependencies: - autoprefixer "^10.4.12" - cssnano-preset-default "^5.2.14" - postcss-discard-unused "^5.1.0" - postcss-merge-idents "^5.1.1" - postcss-reduce-idents "^5.2.0" - postcss-zindex "^5.1.0" - cssnano-preset-advanced@^6.1.2: version "6.1.2" resolved "https://registry.yarnpkg.com/cssnano-preset-advanced/-/cssnano-preset-advanced-6.1.2.tgz#82b090872b8f98c471f681d541c735acf8b94d3f" @@ -15200,7 +14729,7 @@ cssnano-utils@^4.0.2: resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-4.0.2.tgz#56f61c126cd0f11f2eef1596239d730d9fceff3c" integrity sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ== -cssnano@^5.0.1, cssnano@^5.1.15, cssnano@^5.1.8: +cssnano@^5.0.1: version "5.1.15" resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.15.tgz#ded66b5480d5127fcb44dac12ea5a983755136bf" integrity sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw== @@ -15922,6 +15451,13 @@ detect-node@^2.0.4: resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== +detect-package-manager@3.0.2, detect-package-manager@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/detect-package-manager/-/detect-package-manager-3.0.2.tgz#ca34261ab84198072580e93ae86582c575428da9" + integrity sha512-8JFjJHutStYrfWwzfretQoyNGoZVW1Fsrp4JO9spa7h/fBfwgTMEIy4/LBzRDGsxwVPHU0q+T9YvwLDJoOApLQ== + dependencies: + execa "^5.1.1" + detect-package-manager@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/detect-package-manager/-/detect-package-manager-2.0.1.tgz#6b182e3ae5e1826752bfef1de9a7b828cffa50d8" @@ -15929,13 +15465,6 @@ detect-package-manager@^2.0.1: dependencies: execa "^5.1.1" -detect-package-manager@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/detect-package-manager/-/detect-package-manager-3.0.2.tgz#ca34261ab84198072580e93ae86582c575428da9" - integrity sha512-8JFjJHutStYrfWwzfretQoyNGoZVW1Fsrp4JO9spa7h/fBfwgTMEIy4/LBzRDGsxwVPHU0q+T9YvwLDJoOApLQ== - dependencies: - execa "^5.1.1" - detect-port-alt@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" @@ -16073,34 +15602,10 @@ doctrine@^2.1.0: dependencies: esutils "^2.0.2" -docusaurus-plugin-openapi-docs@3.0.0-beta.10: - version "3.0.0-beta.10" - resolved "https://registry.yarnpkg.com/docusaurus-plugin-openapi-docs/-/docusaurus-plugin-openapi-docs-3.0.0-beta.10.tgz#f0c303ee852487c852c163d019678a92b53abf81" - integrity sha512-BtMBH4TzCiMM0WbO2ZAMXSuL7Ge9yyASZhAycb5vX+KSUnUgp47/Ex2f6/evBfaadnr6vXYEr1UBT1fSiJYh5w== - dependencies: - "@apidevtools/json-schema-ref-parser" "^11.5.4" - "@docusaurus/plugin-content-docs" "^3.0.1" - "@docusaurus/utils" "^3.0.1" - "@docusaurus/utils-validation" "^3.0.1" - "@paloaltonetworks/openapi-to-postmanv2" "3.1.0-hotfix.1" - "@paloaltonetworks/postman-collection" "^4.1.0" - "@redocly/openapi-core" "^1.10.5" - chalk "^4.1.2" - clsx "^1.1.1" - fs-extra "^9.0.1" - json-pointer "^0.6.2" - json-schema-merge-allof "^0.8.1" - json5 "^2.2.3" - lodash "^4.17.20" - mustache "^4.2.0" - slugify "^1.6.5" - swagger2openapi "^7.0.8" - xml-formatter "^2.6.1" - -docusaurus-plugin-openapi-docs@^3.0.0-beta.10: - version "3.0.2" - resolved "https://registry.yarnpkg.com/docusaurus-plugin-openapi-docs/-/docusaurus-plugin-openapi-docs-3.0.2.tgz#45f45ed187b04127edc682f33a2c56c725fc0f7b" - integrity sha512-58aYOOD6VhJpACvzSdI2fSFbX9H9PA7Xt+O5TfNRMyl5XuSIyrVhqfLr1VqTcBkc4xqA5MGko7JLBIxymXxPvg== +docusaurus-plugin-openapi-docs@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/docusaurus-plugin-openapi-docs/-/docusaurus-plugin-openapi-docs-4.0.1.tgz#2c767cd7af363b24413f7249e85b26ac154d803a" + integrity sha512-ST0VLbRMTNz2O0NFIezWcF0dNYrGf34/oUmn3wH3hdMcStGQIOCEwD3JvuzyQ7WygjAR8md2kITHeRBRB2yhAA== dependencies: "@apidevtools/json-schema-ref-parser" "^11.5.4" "@docusaurus/plugin-content-docs" "^3.0.1" @@ -16128,24 +15633,24 @@ docusaurus-plugin-sass@^0.2.3: dependencies: sass-loader "^10.1.1" -docusaurus-theme-openapi-docs@3.0.0-beta.10: - version "3.0.0-beta.10" - resolved "https://registry.yarnpkg.com/docusaurus-theme-openapi-docs/-/docusaurus-theme-openapi-docs-3.0.0-beta.10.tgz#f9a790b1ef88ff01f266224064f4b2f80d0892ff" - integrity sha512-8oUMMZSrRJ9EssrjWwbM9aYuHOt1AAm6wQDzWr8k6VvefGvVAibg4Y9PK7GeZ243lJikq9s45KqUA0SMwsm+Fg== +docusaurus-theme-openapi-docs@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/docusaurus-theme-openapi-docs/-/docusaurus-theme-openapi-docs-4.0.1.tgz#fb10e9e253afa7d1012f47e73e9a89ab0e8121a5" + integrity sha512-4HIzYm2Y+pPiqvFs2oSEghtSgamza3Az1nGgwAJ+dpowfdOUafsGnbWOkJoFWVncRNn8/2mYSwrbUuo1t0kVUQ== dependencies: "@docusaurus/theme-common" "^3.0.1" "@hookform/error-message" "^2.0.1" - "@paloaltonetworks/postman-code-generators" "1.1.15-patch.2" - "@paloaltonetworks/postman-collection" "^4.1.0" "@reduxjs/toolkit" "^1.7.1" clsx "^1.1.1" copy-text-to-clipboard "^3.1.0" crypto-js "^4.1.1" - docusaurus-plugin-openapi-docs "^3.0.0-beta.10" + docusaurus-plugin-openapi-docs "^4.0.1" docusaurus-plugin-sass "^0.2.3" file-saver "^2.0.5" lodash "^4.17.20" node-polyfill-webpack-plugin "^2.0.1" + postman-code-generators "^1.10.1" + postman-collection "^4.4.0" prism-react-renderer "^2.3.0" react-hook-form "^7.43.8" react-live "^4.0.0" @@ -16247,7 +15752,7 @@ domutils@^2.5.2, domutils@^2.8.0: domelementtype "^2.2.0" domhandler "^4.2.0" -domutils@^3.0.1, domutils@^3.1.0: +domutils@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e" integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== @@ -16495,14 +16000,6 @@ encodeurl@~2.0.0: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== -encoding-sniffer@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz#799569d66d443babe82af18c9f403498365ef1d5" - integrity sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg== - dependencies: - iconv-lite "^0.6.3" - whatwg-encoding "^3.1.1" - encoding@^0.1.12: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" @@ -16533,16 +16030,16 @@ endent@^2.0.1: fast-json-parse "^1.0.3" objectorarray "^1.0.5" -engine.io-client@~6.5.2: - version "6.5.4" - resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.5.4.tgz#b8bc71ed3f25d0d51d587729262486b4b33bd0d0" - integrity sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ== +engine.io-client@~6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.6.1.tgz#28a9cc4e90d448e1d0ba9369ad08a7af82f9956a" + integrity sha512-aYuoak7I+R83M/BBPIOs2to51BmFIpC1wZe6zZzMrT2llVsHy5cvcmdsJgP2Qz6smHu+sD9oexiSUAVd8OfBPw== dependencies: "@socket.io/component-emitter" "~3.1.0" debug "~4.3.1" engine.io-parser "~5.2.1" ws "~8.17.1" - xmlhttprequest-ssl "~2.0.0" + xmlhttprequest-ssl "~2.1.1" engine.io-parser@~5.2.1: version "5.2.3" @@ -20551,15 +20048,15 @@ htmlparser2@^7.2.0: domutils "^2.8.0" entities "^3.0.1" -htmlparser2@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-9.1.0.tgz#cdb498d8a75a51f739b61d3f718136c369bc8c23" - integrity sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ== +htmlparser2@^8.0.1: + version "8.0.2" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.2.tgz#f002151705b383e62433b5cf466f5b716edaec21" + integrity sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA== dependencies: domelementtype "^2.3.0" domhandler "^5.0.3" - domutils "^3.1.0" - entities "^4.5.0" + domutils "^3.0.1" + entities "^4.4.0" http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: version "4.1.1" @@ -20787,7 +20284,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6.3, iconv-lite@^0.6.2, iconv-lite@^0.6.3: +iconv-lite@0.6.3, iconv-lite@^0.6.2: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -20949,10 +20446,10 @@ infer-owner@^1.0.3, infer-owner@^1.0.4: resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== -infima@0.2.0-alpha.43: - version "0.2.0-alpha.43" - resolved "https://registry.yarnpkg.com/infima/-/infima-0.2.0-alpha.43.tgz#f7aa1d7b30b6c08afef441c726bac6150228cbe0" - integrity sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ== +infima@0.2.0-alpha.44: + version "0.2.0-alpha.44" + resolved "https://registry.yarnpkg.com/infima/-/infima-0.2.0-alpha.44.tgz#9cd9446e473b44d49763f48efabe31f32440861d" + integrity sha512-tuRkUSO/lB3rEhLJk25atwAjgLuzq070+pOW8XcvpHky/YbENnRRdPd85IBkyeTgttmOy5ah+yHYsK1HhUd4lQ== inflight@^1.0.4: version "1.0.6" @@ -21280,7 +20777,7 @@ is-buffer@^2.0.0: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== -is-builtin-module@^3.1.0, is-builtin-module@^3.2.1: +is-builtin-module@^3.1.0: version "3.2.1" resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== @@ -22827,7 +22324,7 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^29.1.2, jest-worker@^29.4.3, jest-worker@^29.7.0: +jest-worker@^29.4.3, jest-worker@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== @@ -25344,11 +24841,6 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.51.0: - version "1.51.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" - integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== - mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -25378,13 +24870,6 @@ mime-types@2.1.18: dependencies: mime-db "~1.33.0" -mime-types@2.1.34: - version "2.1.34" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" - integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== - dependencies: - mime-db "1.51.0" - mime-types@2.1.35, mime-types@^2.1.12, mime-types@^2.1.16, mime-types@^2.1.27, mime-types@^2.1.30, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" @@ -26657,18 +26142,6 @@ oas-linter@^3.2.2: should "^13.2.1" yaml "^1.10.0" -oas-resolver-browser@2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/oas-resolver-browser/-/oas-resolver-browser-2.5.2.tgz#d972525a840d7a74ab1aa43e215e9531a99412ba" - integrity sha512-L3ugWyBHOpKLT+lb+pFXCOpk3byh6usis5T9u9mfu92jH5bR6YK8MA2bebUTIjY7I4415PzDeZcmcc+i7X05MA== - dependencies: - node-fetch-h2 "^2.3.0" - oas-kit-common "^1.0.8" - path-browserify "^1.0.1" - reftools "^1.1.6" - yaml "^1.10.0" - yargs "^15.3.1" - oas-resolver-browser@2.5.6: version "2.5.6" resolved "https://registry.yarnpkg.com/oas-resolver-browser/-/oas-resolver-browser-2.5.6.tgz#1974db66d594fa8c67d3aa866b46b9e2156a8b55" @@ -27495,13 +26968,6 @@ parse5-htmlparser2-tree-adapter@^7.0.0: domhandler "^5.0.2" parse5 "^7.0.0" -parse5-parser-stream@^7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz#d7c20eadc37968d272e2c02660fff92dd27e60e1" - integrity sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow== - dependencies: - parse5 "^7.0.0" - parse5@5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" @@ -27512,7 +26978,7 @@ parse5@^6.0.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== -parse5@^7.0.0, parse5@^7.1.2: +parse5@^7.0.0: version "7.1.2" resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== @@ -27693,7 +27159,7 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -path@^0.12.7: +path@0.12.7, path@^0.12.7: version "0.12.7" resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" integrity sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q== @@ -28131,13 +27597,6 @@ postcss-discard-overridden@^6.0.2: resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz#4e9f9c62ecd2df46e8fdb44dc17e189776572e2d" integrity sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ== -postcss-discard-unused@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-5.1.0.tgz#8974e9b143d887677304e558c1166d3762501142" - integrity sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw== - dependencies: - postcss-selector-parser "^6.0.5" - postcss-discard-unused@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-6.0.5.tgz#c1b0e8c032c6054c3fbd22aaddba5b248136f338" @@ -28204,14 +27663,6 @@ postcss-loader@^7.3.3: jiti "^1.20.0" semver "^7.5.4" -postcss-merge-idents@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-5.1.1.tgz#7753817c2e0b75d0853b56f78a89771e15ca04a1" - integrity sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw== - dependencies: - cssnano-utils "^3.1.0" - postcss-value-parser "^4.2.0" - postcss-merge-idents@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz#7b9c31c7bc823c94bec50f297f04e3c2b838ea65" @@ -28543,13 +27994,6 @@ postcss-ordered-values@^6.0.2: cssnano-utils "^4.0.2" postcss-value-parser "^4.2.0" -postcss-reduce-idents@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-5.2.0.tgz#c89c11336c432ac4b28792f24778859a67dfba95" - integrity sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg== - dependencies: - postcss-value-parser "^4.2.0" - postcss-reduce-idents@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-6.0.3.tgz#b0d9c84316d2a547714ebab523ec7d13704cd486" @@ -28603,13 +28047,6 @@ postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.11, postcss-selecto cssesc "^3.0.0" util-deprecate "^1.0.2" -postcss-sort-media-queries@^4.4.1: - version "4.4.1" - resolved "https://registry.yarnpkg.com/postcss-sort-media-queries/-/postcss-sort-media-queries-4.4.1.tgz#04a5a78db3921eb78f28a1a781a2e68e65258128" - integrity sha512-QDESFzDDGKgpiIh4GYXsSy6sek2yAwQx1JASl5AxBtU1Lq2JfKBljIPNdil989NcSKRQX1ToiaKphImtBuhXWw== - dependencies: - sort-css-media-queries "2.1.0" - postcss-sort-media-queries@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/postcss-sort-media-queries/-/postcss-sort-media-queries-5.2.0.tgz#4556b3f982ef27d3bac526b99b6c0d3359a6cf97" @@ -28652,11 +28089,6 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss-zindex@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-5.1.0.tgz#4a5c7e5ff1050bd4c01d95b1847dfdcc58a496ff" - integrity sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A== - postcss-zindex@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-6.0.2.tgz#e498304b83a8b165755f53db40e2ea65a99b56e1" @@ -28679,7 +28111,7 @@ postcss@^7.0.14, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.36, postcss@^7.0 picocolors "^0.2.1" source-map "^0.6.1" -postcss@^8.2.15, postcss@^8.4.13, postcss@^8.4.17, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.24, postcss@^8.4.26, postcss@^8.4.31, postcss@^8.4.33, postcss@^8.4.38, postcss@^8.4.43, postcss@^8.4.47: +postcss@^8.2.15, postcss@^8.4.13, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.24, postcss@^8.4.26, postcss@^8.4.31, postcss@^8.4.33, postcss@^8.4.38, postcss@^8.4.43, postcss@^8.4.47: version "8.4.47" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365" integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ== @@ -28737,6 +28169,18 @@ postgres-range@^1.1.1: resolved "https://registry.yarnpkg.com/postgres-range/-/postgres-range-1.1.4.tgz#a59c5f9520909bcec5e63e8cf913a92e4c952863" integrity sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w== +postman-code-generators@^1.10.1: + version "1.13.0" + resolved "https://registry.yarnpkg.com/postman-code-generators/-/postman-code-generators-1.13.0.tgz#ac355caf18f96fac53ad1da08ac8a30c936dc55f" + integrity sha512-rbKtX+PWp+4McQpAncnRCUKqDiynt4fDB1I7AzrsvBAQc74ab6k6K3IP8qvf0icqiPuf9nYHCSdy/LB922dQLQ== + dependencies: + async "3.2.2" + detect-package-manager "3.0.2" + lodash "4.17.21" + path "0.12.7" + postman-collection "^4.4.0" + shelljs "0.8.5" + postman-collection@^4.4.0: version "4.5.0" resolved "https://registry.yarnpkg.com/postman-collection/-/postman-collection-4.5.0.tgz#cc485d67f2177d6f4c5c5f4bc75c257efd23f221" @@ -29451,7 +28895,7 @@ react-docgen@^5.0.0: node-dir "^0.1.10" strip-indent "^3.0.0" -react-dom@^18.0.0, react-dom@^18.2.0: +react-dom@^18.2.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== @@ -29769,7 +29213,7 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" -react@^18.0.0, react@^18.2.0: +react@^18.2.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== @@ -30109,7 +29553,7 @@ reflect.getprototypeof@^1.0.4: globalthis "^1.0.3" which-builtin-type "^1.1.3" -reftools@^1.1.6, reftools@^1.1.9: +reftools@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/reftools/-/reftools-1.1.9.tgz#e16e19f662ccd4648605312c06d34e5da3a2b77e" integrity sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w== @@ -30432,9 +29876,9 @@ remark-rehype@^10.0.0: unified "^10.0.0" remark-rehype@^11.0.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.0.tgz#d5f264f42bcbd4d300f030975609d01a1697ccdc" - integrity sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g== + version "11.1.1" + resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.1.tgz#f864dd2947889a11997c0a2667cd6b38f685bca7" + integrity sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ== dependencies: "@types/hast" "^3.0.0" "@types/mdast" "^4.0.0" @@ -30919,9 +30363,9 @@ rollup@^1.32.1: acorn "^7.1.0" rollup@^3.29.4: - version "3.29.4" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.4.tgz#4d70c0f9834146df8705bfb69a9a19c9e1109981" - integrity sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw== + version "3.29.5" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.5.tgz#8a2e477a758b520fb78daf04bca4c522c1da8a54" + integrity sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w== optionalDependencies: fsevents "~2.3.2" @@ -31558,7 +31002,7 @@ shellac@^0.8.0: dependencies: reghex "^1.0.2" -shelljs@^0.8.3, shelljs@^0.8.5: +shelljs@0.8.5, shelljs@^0.8.3, shelljs@^0.8.5: version "0.8.5" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== @@ -31869,13 +31313,13 @@ snapdragon@^0.8.1: use "^3.1.0" socket.io-client@^4.5.1: - version "4.7.5" - resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.7.5.tgz#919be76916989758bdc20eec63f7ee0ae45c05b7" - integrity sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ== + version "4.8.0" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.8.0.tgz#2ea0302d0032d23422bd2860f78127a800cad6a2" + integrity sha512-C0jdhD5yQahMws9alf/yvtsMGTaIDBnZ8Rb5HU56svyq0l5LIrGzIDZZD5pHQlmzxLuU91Gz+VpQMKgCTNYtkw== dependencies: "@socket.io/component-emitter" "~3.1.0" debug "~4.3.2" - engine.io-client "~6.5.2" + engine.io-client "~6.6.1" socket.io-parser "~4.2.4" socket.io-parser@~4.2.4: @@ -32007,11 +31451,6 @@ sonic-boom@^3.7.0: dependencies: atomic-sleep "^1.0.0" -sort-css-media-queries@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/sort-css-media-queries/-/sort-css-media-queries-2.1.0.tgz#7c85e06f79826baabb232f5560e9745d7a78c4ce" - integrity sha512-IeWvo8NkNiY2vVYdPa27MCQiR0MN0M80johAYFVxWWXQ44KU84WNxjslwBHmc/7ZL2ccwkM7/e6S5aiKZXm7jA== - sort-css-media-queries@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz#aa33cf4a08e0225059448b6c40eddbf9f1c8334c" @@ -33018,7 +32457,7 @@ svg-tags@1: resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA== -svgo@^2.7.0, svgo@^2.8.0: +svgo@^2.7.0: version "2.8.0" resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== @@ -33142,6 +32581,16 @@ symbol.prototype.description@^1.0.0: has-symbols "^1.0.3" object.getownpropertydescriptors "^2.1.7" +synapse-constants@1.3.22: + version "1.3.22" + resolved "https://registry.yarnpkg.com/synapse-constants/-/synapse-constants-1.3.22.tgz#e885e12870909b62b1484db007868b8b59648dd7" + integrity sha512-PnEuKh8LBujoqOW+VzbQPkAvDld0aBxQUcnN7f0HeSxoF43I5gHyTYngm86qmgf3D+9rPplrigYtVscIBR8NfQ== + dependencies: + "@codecov/webpack-plugin" "^0.0.1-beta.10" + copyfiles "^2.4.1" + ethers "5.7.2" + lodash "^4.17.21" + synchronous-promise@^2.0.15: version "2.0.17" resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.17.tgz#38901319632f946c982152586f2caf8ddc25c032" @@ -34435,11 +33884,6 @@ undici@^5.14.0, undici@^5.25.4, undici@^5.28.4: dependencies: "@fastify/busboy" "^2.0.0" -undici@^6.19.5: - version "6.19.8" - resolved "https://registry.yarnpkg.com/undici/-/undici-6.19.8.tgz#002d7c8a28f8cc3a44ff33c3d4be4d85e15d40e1" - integrity sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g== - unenv@^1.9.0: version "1.10.0" resolved "https://registry.yarnpkg.com/unenv/-/unenv-1.10.0.tgz#c3394a6c6e4cfe68d699f87af456fe3f0db39571" @@ -35654,7 +35098,7 @@ web3-core@1.7.4: web3-core-requestmanager "1.7.4" web3-utils "1.7.4" -web3-core@^4.3.0, web3-core@^4.4.0, web3-core@^4.5.0, web3-core@^4.5.1: +web3-core@^4.4.0, web3-core@^4.5.0, web3-core@^4.5.1: version "4.5.1" resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.5.1.tgz#aba57c7f925fc2d72e2109eef5ff0ecaa4482b86" integrity sha512-mFMOO/IWdKsLL1o2whh3oJ0LCG9P3l5c4lpiMoVsVln3QXh/B0Gf8gW3aY8S+Ixm0OHyzFDXJVc2CodxqmI4Gw== @@ -35670,6 +35114,22 @@ web3-core@^4.3.0, web3-core@^4.4.0, web3-core@^4.5.0, web3-core@^4.5.1: optionalDependencies: web3-providers-ipc "^4.0.7" +web3-core@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.6.0.tgz#1b3e88ed35142b4d6fcbc7737e1d71943f99cf45" + integrity sha512-j8uQ/7zSwpmLClMMeZb736Ok3V4cWSd0dnd29jkd10d1pedi32r+hSAgycxSJLLWtPHOzMBIXUjj3TF/IAClVQ== + dependencies: + web3-errors "^1.3.0" + web3-eth-accounts "^4.2.1" + web3-eth-iban "^4.0.7" + web3-providers-http "^4.2.0" + web3-providers-ws "^4.0.8" + web3-types "^1.8.0" + web3-utils "^4.3.1" + web3-validator "^2.0.6" + optionalDependencies: + web3-providers-ipc "^4.0.7" + web3-errors@^1.1.3, web3-errors@^1.2.0, web3-errors@^1.2.1, web3-errors@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/web3-errors/-/web3-errors-1.3.0.tgz#504e4d3218899df108856940087a8022d6688d74" @@ -35704,6 +35164,17 @@ web3-eth-abi@^4.2.3: web3-utils "^4.3.1" web3-validator "^2.0.6" +web3-eth-abi@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-4.2.4.tgz#b66f4b067ba06c0aecc013e98a4d717547ab8174" + integrity sha512-FGoj/ENm/Iq3+6myJyiDCwbFkha9ZCx2fRdiIdw3mp7S4lgu+ay3EVzQPRxJjNBm09UEfxB9yoSAPKj9Z3Mbxg== + dependencies: + abitype "0.7.1" + web3-errors "^1.3.0" + web3-types "^1.8.0" + web3-utils "^4.3.1" + web3-validator "^2.0.6" + web3-eth-accounts@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.10.0.tgz#2942beca0a4291455f32cf09de10457a19a48117" @@ -35886,16 +35357,16 @@ web3-eth-personal@1.7.4: web3-utils "1.7.4" web3-eth-personal@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-4.0.8.tgz#b51628c560de550ca8b354fa784f9556aae6065c" - integrity sha512-sXeyLKJ7ddQdMxz1BZkAwImjqh7OmKxhXoBNF3isDmD4QDpMIwv/t237S3q4Z0sZQamPa/pHebJRWVuvP8jZdw== + version "4.1.0" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-4.1.0.tgz#f5b506a4570bf1241d1db2de12cb413ea0bb4486" + integrity sha512-RFN83uMuvA5cu1zIwwJh9A/bAj0OBxmGN3tgx19OD/9ygeUZbifOL06jgFzN0t+1ekHqm3DXYQM8UfHpXi7yDQ== dependencies: - web3-core "^4.3.0" - web3-eth "^4.3.1" - web3-rpc-methods "^1.1.3" - web3-types "^1.3.0" - web3-utils "^4.0.7" - web3-validator "^2.0.3" + web3-core "^4.6.0" + web3-eth "^4.9.0" + web3-rpc-methods "^1.3.0" + web3-types "^1.8.0" + web3-utils "^4.3.1" + web3-validator "^2.0.6" web3-eth@1.10.0: version "1.10.0" @@ -35933,7 +35404,7 @@ web3-eth@1.7.4: web3-net "1.7.4" web3-utils "1.7.4" -web3-eth@^4.3.1, web3-eth@^4.8.0, web3-eth@^4.8.2: +web3-eth@^4.8.0, web3-eth@^4.8.2: version "4.8.2" resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-4.8.2.tgz#5459aff239a6f27816a522570bdd86732856ef0f" integrity sha512-DLV/fIMG6gBp/B0gv0+G4FzxZ4YCDQsY3lzqqv7avwh3uU7/O27aifCUcFd7Ye+3ixTqCjAvLEl9wYSeyG3zQw== @@ -35950,6 +35421,23 @@ web3-eth@^4.3.1, web3-eth@^4.8.0, web3-eth@^4.8.2: web3-utils "^4.3.1" web3-validator "^2.0.6" +web3-eth@^4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-4.9.0.tgz#324403d913cc29bcae6cc1ad50a6defeb762828a" + integrity sha512-lE+5rQUkQq1Mzf3uZ/tlay8nvMyC/CmaRFRFQ015OZuvSrRr/byZhhkzY5ZWkIetESTMqfWapu67yeHebcHxwA== + dependencies: + setimmediate "^1.0.5" + web3-core "^4.6.0" + web3-errors "^1.3.0" + web3-eth-abi "^4.2.4" + web3-eth-accounts "^4.2.1" + web3-net "^4.1.0" + web3-providers-ws "^4.0.8" + web3-rpc-methods "^1.3.0" + web3-types "^1.8.0" + web3-utils "^4.3.1" + web3-validator "^2.0.6" + web3-net@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.10.0.tgz#be53e7f5dafd55e7c9013d49c505448b92c9c97b" @@ -36061,7 +35549,7 @@ web3-providers-ws@^4.0.8: web3-utils "^4.3.1" ws "^8.17.1" -web3-rpc-methods@^1.1.3, web3-rpc-methods@^1.3.0: +web3-rpc-methods@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/web3-rpc-methods/-/web3-rpc-methods-1.3.0.tgz#d5ee299a69389d63822d354ddee2c6a121a6f670" integrity sha512-/CHmzGN+IYgdBOme7PdqzF+FNeMleefzqs0LVOduncSaqsppeOEoskLXb2anSpzmQAP3xZJPaTrkQPWSJMORig== @@ -36107,6 +35595,11 @@ web3-types@^1.3.0, web3-types@^1.6.0, web3-types@^1.7.0: resolved "https://registry.yarnpkg.com/web3-types/-/web3-types-1.7.0.tgz#9945fa644af96b20b1db18564aff9ab8db00df59" integrity sha512-nhXxDJ7a5FesRw9UG5SZdP/C/3Q2EzHGnB39hkAV+YGXDMgwxBXFWebQLfEzZzuArfHnvC0sQqkIHNwSKcVjdA== +web3-types@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/web3-types/-/web3-types-1.8.0.tgz#d2151fd9e87d711ef5a13079885665b458243e46" + integrity sha512-Z51wFLPGhZM/1uDxrxE8gzju3t2aEdRGn+YmLX463id5UjTuMEmP/9in1GFjqrsPB3m86czs8RnGBUt3ovueMw== + web3-utils@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.0.tgz#ca4c1b431a765c14ac7f773e92e0fd9377ccf578" @@ -36389,7 +35882,7 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: source-list-map "^2.0.0" source-map "~0.6.1" -webpack-sources@^3.2.2, webpack-sources@^3.2.3: +webpack-sources@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== @@ -36512,23 +36005,11 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: dependencies: iconv-lite "0.4.24" -whatwg-encoding@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" - integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== - dependencies: - iconv-lite "0.6.3" - whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-mimetype@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" - integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== - whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -36956,10 +36437,10 @@ xmlchars@^2.1.1: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xmlhttprequest-ssl@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67" - integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A== +xmlhttprequest-ssl@~2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.1.tgz#0d045c3b2babad8e7db1af5af093f5d0d60df99a" + integrity sha512-ptjR8YSJIXoA3Mbv5po7RtSYHO6mZr8s7i5VGmEk7QY2pQWyT1o0N+W1gKbOyJPUCGXGnuw0wqe8f0L6Y0ny7g== xmlhttprequest@1.8.0: version "1.8.0" From f246d33d7967869e8d34a3a49eb2e08ed10d40e5 Mon Sep 17 00:00:00 2001 From: abtestingalpha Date: Wed, 2 Oct 2024 16:27:05 +0000 Subject: [PATCH 82/90] Publish - @synapsecns/bridge-docs@0.3.0 - @synapsecns/synapse-interface@0.40.3 --- docs/bridge/CHANGELOG.md | 11 +++++++++++ docs/bridge/package.json | 2 +- packages/synapse-interface/CHANGELOG.md | 8 ++++++++ packages/synapse-interface/package.json | 2 +- 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/bridge/CHANGELOG.md b/docs/bridge/CHANGELOG.md index c83abed722..26fd806986 100644 --- a/docs/bridge/CHANGELOG.md +++ b/docs/bridge/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.3.0](https://github.com/synapsecns/sanguine/compare/@synapsecns/bridge-docs@0.2.4...@synapsecns/bridge-docs@0.3.0) (2024-10-02) + + +### Features + +* **rfq:** relay rate limiting ([#2933](https://github.com/synapsecns/sanguine/issues/2933)) [SLT-149] ([a240292](https://github.com/synapsecns/sanguine/commit/a2402928f7e56369ce39cc6f211c3e3b3dfd404e)) + + + + + ## [0.2.4](https://github.com/synapsecns/sanguine/compare/@synapsecns/bridge-docs@0.2.3...@synapsecns/bridge-docs@0.2.4) (2024-09-09) **Note:** Version bump only for package @synapsecns/bridge-docs diff --git a/docs/bridge/package.json b/docs/bridge/package.json index 80dca5e89d..34caa4f315 100644 --- a/docs/bridge/package.json +++ b/docs/bridge/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/bridge-docs", - "version": "0.2.4", + "version": "0.3.0", "private": true, "scripts": { "docusaurus": "docusaurus", diff --git a/packages/synapse-interface/CHANGELOG.md b/packages/synapse-interface/CHANGELOG.md index 0358d280dc..4ebfa78640 100644 --- a/packages/synapse-interface/CHANGELOG.md +++ b/packages/synapse-interface/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.40.3](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.40.2...@synapsecns/synapse-interface@0.40.3) (2024-10-02) + +**Note:** Version bump only for package @synapsecns/synapse-interface + + + + + ## [0.40.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.40.1...@synapsecns/synapse-interface@0.40.2) (2024-10-01) **Note:** Version bump only for package @synapsecns/synapse-interface diff --git a/packages/synapse-interface/package.json b/packages/synapse-interface/package.json index d789fa45fa..a620c8472d 100644 --- a/packages/synapse-interface/package.json +++ b/packages/synapse-interface/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/synapse-interface", - "version": "0.40.2", + "version": "0.40.3", "private": true, "engines": { "node": ">=18.18.0" From 12668b55c03135b501456689e2d07c9e07aab815 Mon Sep 17 00:00:00 2001 From: parodime Date: Wed, 2 Oct 2024 14:14:44 -0400 Subject: [PATCH 83/90] FbV2 sender nonces [SLT-183] (#3214) * sender nonces [SLT-183] * refactor nonce test for codecov * refactor: make `nonce` an immutable var * chore: ignore local coverage report --------- Co-authored-by: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> --- packages/contracts-rfq/.gitignore | 4 +++- .../contracts-rfq/contracts/FastBridgeV2.sol | 12 +++++++----- .../test/FastBridgeV2.Src.Base.t.sol | 6 ++++++ .../contracts-rfq/test/FastBridgeV2.Src.t.sol | 18 +++++++++++++++++- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/packages/contracts-rfq/.gitignore b/packages/contracts-rfq/.gitignore index 5726149ffb..7c3b19de11 100644 --- a/packages/contracts-rfq/.gitignore +++ b/packages/contracts-rfq/.gitignore @@ -1,3 +1,5 @@ flattened .deployments -broadcast \ No newline at end of file +broadcast + +lcov.info \ No newline at end of file diff --git a/packages/contracts-rfq/contracts/FastBridgeV2.sol b/packages/contracts-rfq/contracts/FastBridgeV2.sol index 67221da8d8..5a764a7c95 100644 --- a/packages/contracts-rfq/contracts/FastBridgeV2.sol +++ b/packages/contracts-rfq/contracts/FastBridgeV2.sol @@ -28,11 +28,13 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { mapping(bytes32 => BridgeTxDetails) public bridgeTxDetails; /// @notice Relay details on destination chain mapping(bytes32 => BridgeRelay) public bridgeRelayDetails; + /// @notice Unique bridge nonces tracked per originSender + mapping(address => uint256) public senderNonces; - /// @dev to prevent replays - uint256 public nonce; - - // @dev the block the contract was deployed at + /// @notice This is deprecated and should not be used. + /// @dev Replaced by senderNonces + uint256 public immutable nonce = 0; + /// @notice the block the contract was deployed at uint256 public immutable deployBlock; constructor(address _owner) Admin(_owner) { @@ -158,7 +160,7 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { originFeeAmount: originFeeAmount, sendChainGas: params.sendChainGas, deadline: params.deadline, - nonce: nonce++, // increment nonce on every bridge + nonce: senderNonces[params.sender]++, // increment nonce on every bridge exclusivityRelayer: paramsV2.quoteRelayer, // We checked exclusivityEndTime to be in range (0 .. params.deadline] above, so can safely cast exclusivityEndTime: uint256(exclusivityEndTime) diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol index 9a45037d54..e63f292194 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol @@ -95,6 +95,12 @@ abstract contract FastBridgeV2SrcBaseTest is FastBridgeV2Test { fastBridge.refund(abi.encode(bridgeTx)); } + function test_nonce() public view { + uint256 result = fastBridge.nonce(); + // deprecated. should always return zero in FbV2. + assertEq(result, 0); + } + function assertEq(FastBridgeV2.BridgeStatus a, FastBridgeV2.BridgeStatus b) public pure { assertEq(uint8(a), uint8(b)); } diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol index dd01387d72..c2a5902133 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol @@ -103,6 +103,8 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { expectBridgeRequested(tokenTx, txId); expectBridgeQuoteDetails(txId, tokenParamsV2.quoteId); bridge({caller: userA, msgValue: 0, params: tokenParams}); + assertEq(fastBridge.senderNonces(userA), 1); + assertEq(fastBridge.senderNonces(userB), 0); assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); checkTokenBalancesAfterBridge(userA); } @@ -112,6 +114,8 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { expectBridgeRequested(tokenTx, txId); expectBridgeQuoteDetails(txId, tokenParamsV2.quoteId); bridge({caller: userB, msgValue: 0, params: tokenParams}); + assertEq(fastBridge.senderNonces(userA), 1); + assertEq(fastBridge.senderNonces(userB), 0); assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + tokenParams.originAmount); checkTokenBalancesAfterBridge(userB); @@ -126,10 +130,14 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { function test_bridge_eth() public { // bridge token first to match the nonce bridge({caller: userA, msgValue: 0, params: tokenParams}); + assertEq(fastBridge.senderNonces(userA), 1); + assertEq(fastBridge.senderNonces(userB), 0); bytes32 txId = getTxId(ethTx); expectBridgeRequested(ethTx, txId); expectBridgeQuoteDetails(txId, ethParamsV2.quoteId); bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + assertEq(fastBridge.senderNonces(userA), 2); + assertEq(fastBridge.senderNonces(userB), 0); assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); checkEthBalancesAfterBridge(userA); } @@ -137,18 +145,24 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { function test_bridge_eth_diffSender() public { // bridge token first to match the nonce bridge({caller: userA, msgValue: 0, params: tokenParams}); + assertEq(fastBridge.senderNonces(userA), 1); + assertEq(fastBridge.senderNonces(userB), 0); bytes32 txId = getTxId(ethTx); expectBridgeRequested(ethTx, txId); expectBridgeQuoteDetails(txId, ethParamsV2.quoteId); + // bridge for user A as sender, called by userB bridge({caller: userB, msgValue: ethParams.originAmount, params: ethParams}); + assertEq(fastBridge.senderNonces(userA), 2); + assertEq(fastBridge.senderNonces(userB), 0); assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); assertEq(userA.balance, LEFTOVER_BALANCE + ethParams.originAmount); checkEthBalancesAfterBridge(userB); } function test_bridge_userSpecificNonce() public { - vm.skip(true); // TODO: unskip when implemented bridge({caller: userA, msgValue: 0, params: tokenParams}); + assertEq(fastBridge.senderNonces(userA), 1); + assertEq(fastBridge.senderNonces(userB), 0); // UserB nonce is 0 ethTx.nonce = 0; ethParams.sender = userB; @@ -157,6 +171,8 @@ contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { expectBridgeRequested(ethTx, txId); expectBridgeQuoteDetails(txId, ethParamsV2.quoteId); bridge({caller: userB, msgValue: ethParams.originAmount, params: ethParams}); + assertEq(fastBridge.senderNonces(userA), 1); + assertEq(fastBridge.senderNonces(userB), 1); assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); checkEthBalancesAfterBridge(userB); } From da18f00edaeb52d0e51af811113615f1352a3e3d Mon Sep 17 00:00:00 2001 From: ChiTimesChi Date: Wed, 2 Oct 2024 18:18:48 +0000 Subject: [PATCH 84/90] Publish - @synapsecns/contracts-rfq@0.6.1 --- packages/contracts-rfq/CHANGELOG.md | 8 ++++++++ packages/contracts-rfq/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/contracts-rfq/CHANGELOG.md b/packages/contracts-rfq/CHANGELOG.md index ee07f57c48..d94a85a1b9 100644 --- a/packages/contracts-rfq/CHANGELOG.md +++ b/packages/contracts-rfq/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.6.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.6.0...@synapsecns/contracts-rfq@0.6.1) (2024-10-02) + +**Note:** Version bump only for package @synapsecns/contracts-rfq + + + + + # [0.6.0](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.10...@synapsecns/contracts-rfq@0.6.0) (2024-10-01) diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index e48b1be68c..6af70089e9 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/contracts-rfq", "license": "MIT", - "version": "0.6.0", + "version": "0.6.1", "description": "FastBridge contracts.", "private": true, "files": [ From ad48cb0f026d6722d851e31594cadfb78d82a949 Mon Sep 17 00:00:00 2001 From: dwasse Date: Wed, 2 Oct 2024 14:56:49 -0500 Subject: [PATCH 85/90] feat(rfq): active quoting API (#3128) * WIP: initial websocket wiring * WIP: add ws client and handling * Fix: receive respsects context * Cleanup: split into rfq.go * Fix: build * Feat: add mocked ws client * Fix: build * Feat: add SubscribeActiveQuotes() to client * Feat: add PutUserQuoteRequest() to api client * Fix: build * WIP: rfq tests with ws auth * WIP: test fixes * Feat: working TestHandleActiveRFQ * Feat: add expired request case * WIP: functionalize test relayer resps * Feat: add runMockRelayer with multiple relayers * Feat: add MultipleRelayers case * Feat: add FallbackToPassive case * Fix: bigint ptr issue * Cleanup: bump expiration window * WIP: logs * Feat: split into separate tests * Cleanup: logs * Feat: add PassiveBestQuote case * WIP: update db interface with new models * Feat: impl new db funcs * Feat: insert models within api server * Feat: update quote request / response statuses * Fix: db error handling * Fix: api tests * Feat: add initial response validation * Feat: impl pingpong * Fix: register models * Feat: verify quote request in SingleRelayer case * Feat: verify more db requests * Cleanup: common vars * Cleanup: break down handleActiveRFQ into sub funcs * Cleanup: comments * Cleanup: remove unused mock * Fix: builds * Feat: make relayer response data optional to signify null resp * Fix: response primary key on quote id * Fix: build * Feat: update swagger docs * WIP: generic pubsub * Feat: add basic PubSubManager * Feat: implement subscription / unsubscription operations * Feat: respond to subscribe operation * Feat: add runWsListener helper * Cleanup: reduce chan buffer * Cleanup: lints * Cleanup: break down into smaller funcs * Cleanup: refactor ws client * Cleanup: more lints * Fix: build * Cleanup: lints * Feat: mark as fulfilled when updating request status * Cleanup: lint * Skip broken test for now * Cleanup: lint * Feat: add open_quote_requests endpoint with test * Feat: add new open request model * Update swagger * go mod tidy * fix error * Fix: respecting context * Replace: Fulfilled -> Closed * Cleanup: use errors.New() * Feat: ReceiveQuoteResponse specifies request id * Cleanup: remove logs * Feat: add some tracing * Feat: add IntegratorID * Feat: remove repetitive fields from relayer quote response, move requests to requests.go * Cleanup: use new routes * Cleanup: migrate req/res struct naming * Cleanup: update swagger * Cleanup: lint * [goreleaser] * Feat: run ws endpoint within existing server * [goreleaser] * Fix: build * [goreleaser] * Feat: add more tracing * [goreleaser] * feat(rfq-relayer): relayer supports active quoting (#3198) * Feat: add active rfq subscription on quoter * Feat: relayer subscribes to active quotes upon starting * [goreleaser] * Feat: specify ws url in relayer * [goreleaser] * [goreleaser] * Fix: build * [goreleaser] * Feat: relayer tracing * [goreleaser] * Feat: use supports_active_quoting instead of ws url * [goreleaser] * WIP: add logs * [goreleaser] * WIP: more logs * [goreleaser] * More logs * [goreleaser] * More logs * [goreleaser] * More logs * [goreleaser] * Close conn when encountering write err * [goreleaser] * More logs * [goreleaser] * More logs * [goreleaser] * More logs * [goreleaser] * More logs * [goreleaser] * Logs with ts * [goreleaser] * More tracing * [goreleaser] * Fix: send to reqChan * [goreleaser] * Check for zero pong time * Fix: make close_at and closed_quote_id optional * [goreleaser] * Feat: remove extra fields from responses * [goreleaser] * Fix: skip passive quote * [goreleaser] * Cleanup: remove logs * Fix: use correct span * Cleanup: remove logs * Fix: build * Cleanup: lint * Cleanup: lint * Cleanup: update swagger * Feat: client sends pings, server sends pongs * [goreleaser] * Cleanup: remove unused func * WIP: ws error handling * [goreleaser] * Feat: ws client uses errgroup * Cleanup: remove log * [goreleaser] * Replace: PutUserQuoteResponse -> PutRFQResponse * Feat: add QuoteID to PutRFQResponse * [goreleaser] * Cleanup: lint * Fix: build * Cleanup: lint * [goreleaser] * Add logs * [goreleaser] * Add logs * [goreleaser] * Cleanup: remove logs --------- Co-authored-by: Trajan0x --- contrib/opbot/go.mod | 1 + services/rfq/api/client/client.go | 241 ++++++++++- services/rfq/api/client/client_test.go | 8 +- .../api/db/activequoterequeststatus_string.go | 27 ++ .../db/activequoteresponsestatus_string.go | 28 ++ services/rfq/api/db/api_db.go | 180 ++++++++ services/rfq/api/db/sql/base/base.go | 2 +- services/rfq/api/db/sql/base/store.go | 77 ++++ services/rfq/api/docs/docs.go | 210 +++++++++- services/rfq/api/docs/swagger.json | 210 +++++++++- services/rfq/api/docs/swagger.yaml | 141 ++++++- services/rfq/api/model/request.go | 59 ++- services/rfq/api/model/response.go | 47 +++ services/rfq/api/model/util.go | 24 -- services/rfq/api/rest/auth.go | 2 +- services/rfq/api/rest/handler.go | 63 ++- services/rfq/api/rest/pubsub.go | 80 ++++ services/rfq/api/rest/rfq.go | 272 ++++++++++++ services/rfq/api/rest/rfq_test.go | 389 ++++++++++++++++++ services/rfq/api/rest/server.go | 222 +++++++++- services/rfq/api/rest/server_test.go | 96 ++++- services/rfq/api/rest/suite_test.go | 37 +- services/rfq/api/rest/ws.go | 340 +++++++++++++++ services/rfq/e2e/setup_test.go | 2 +- services/rfq/go.mod | 4 +- services/rfq/relayer/limiter/limiter_test.go | 1 + services/rfq/relayer/quoter/export_test.go | 2 +- services/rfq/relayer/quoter/mocks/quoter.go | 14 + services/rfq/relayer/quoter/quoter.go | 127 +++++- services/rfq/relayer/quoter/quoter_test.go | 6 +- services/rfq/relayer/relconfig/config.go | 6 +- services/rfq/relayer/relconfig/getters.go | 6 +- services/rfq/relayer/service/relayer.go | 12 +- 33 files changed, 2814 insertions(+), 122 deletions(-) create mode 100644 services/rfq/api/db/activequoterequeststatus_string.go create mode 100644 services/rfq/api/db/activequoteresponsestatus_string.go delete mode 100644 services/rfq/api/model/util.go create mode 100644 services/rfq/api/rest/pubsub.go create mode 100644 services/rfq/api/rest/rfq.go create mode 100644 services/rfq/api/rest/rfq_test.go create mode 100644 services/rfq/api/rest/ws.go diff --git a/contrib/opbot/go.mod b/contrib/opbot/go.mod index 71ec290d1a..70e9afa62e 100644 --- a/contrib/opbot/go.mod +++ b/contrib/opbot/go.mod @@ -210,6 +210,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.54.0 // indirect github.com/prometheus/procfs v0.15.0 // indirect + github.com/puzpuzpuz/xsync v1.5.2 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect github.com/richardwilkes/toolbox v1.74.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect diff --git a/services/rfq/api/client/client.go b/services/rfq/api/client/client.go index ce6880b85e..1bcba494b5 100644 --- a/services/rfq/api/client/client.go +++ b/services/rfq/api/client/client.go @@ -4,11 +4,15 @@ package client import ( "context" + "encoding/json" "fmt" "net/http" "strconv" + "strings" "time" + "github.com/ipfs/go-log" + "github.com/google/uuid" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" @@ -17,17 +21,23 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/go-resty/resty/v2" + "github.com/gorilla/websocket" "github.com/synapsecns/sanguine/ethergo/signer/signer" "github.com/synapsecns/sanguine/services/rfq/api/model" "github.com/synapsecns/sanguine/services/rfq/api/rest" ) +var logger = log.Logger("rfq-client") + +const pingPeriod = 20 * time.Second + // AuthenticatedClient is an interface for the RFQ API. // It provides methods for creating, retrieving and updating quotes. type AuthenticatedClient interface { - PutQuote(ctx context.Context, q *model.PutQuoteRequest) error + PutQuote(ctx context.Context, q *model.PutRelayerQuoteRequest) error PutBulkQuotes(ctx context.Context, q *model.PutBulkQuotesRequest) error PutRelayAck(ctx context.Context, req *model.PutAckRequest) (*model.PutRelayAckResponse, error) + SubscribeActiveQuotes(ctx context.Context, req *model.SubscribeActiveRFQRequest, reqChan chan *model.ActiveRFQMessage) (respChan chan *model.ActiveRFQMessage, err error) UnauthenticatedClient } @@ -37,6 +47,7 @@ type UnauthenticatedClient interface { GetSpecificQuote(ctx context.Context, q *model.GetQuoteSpecificRequest) ([]*model.GetQuoteResponse, error) GetQuoteByRelayerAddress(ctx context.Context, relayerAddr string) ([]*model.GetQuoteResponse, error) GetRFQContracts(ctx context.Context) (*model.GetContractsResponse, error) + PutRFQRequest(ctx context.Context, q *model.PutRFQRequest) (*model.PutRFQResponse, error) resty() *resty.Client } @@ -50,7 +61,8 @@ func (c unauthenticatedClient) resty() *resty.Client { type clientImpl struct { UnauthenticatedClient - rClient *resty.Client + rClient *resty.Client + reqSigner signer.Signer } // NewAuthenticatedClient creates a new client for the RFQ quoting API. @@ -65,33 +77,40 @@ func NewAuthenticatedClient(metrics metrics.Handler, rfqURL string, reqSigner si // to a new variable for clarity. authedClient := unauthedClient.resty(). OnBeforeRequest(func(client *resty.Client, request *resty.Request) error { - // if request.Method == "PUT" && request.URL == rfqURL+rest.QUOTE_ROUTE { - // i.e. signature (hex encoded) = keccak(bytes.concat("\x19Ethereum Signed Message:\n", len(strconv.Itoa(time.Now().Unix()), strconv.Itoa(time.Now().Unix()))) - // so that full auth header string: auth = strconv.Itoa(time.Now().Unix()) + ":" + signature - // Get the current Unix timestamp as a string. - now := strconv.Itoa(int(time.Now().Unix())) - - // Prepare the data to be signed. - data := "\x19Ethereum Signed Message:\n" + strconv.Itoa(len(now)) + now - - sig, err := reqSigner.SignMessage(request.Context(), []byte(data), true) - + authHeader, err := getAuthHeader(request.Context(), reqSigner) if err != nil { - return fmt.Errorf("failed to sign request: %w", err) + return fmt.Errorf("failed to get auth header: %w", err) } - - res := fmt.Sprintf("%s:%s", now, hexutil.Encode(signer.Encode(sig))) - request.SetHeader("Authorization", res) - + request.SetHeader(rest.AuthorizationHeader, authHeader) return nil }) return &clientImpl{ UnauthenticatedClient: unauthedClient, rClient: authedClient, + reqSigner: reqSigner, }, nil } +func getAuthHeader(ctx context.Context, reqSigner signer.Signer) (string, error) { + // if request.Method == "PUT" && request.URL == rfqURL+rest.QUOTE_ROUTE { + // i.e. signature (hex encoded) = keccak(bytes.concat("\x19Ethereum Signed Message:\n", len(strconv.Itoa(time.Now().Unix()), strconv.Itoa(time.Now().Unix()))) + // so that full auth header string: auth = strconv.Itoa(time.Now().Unix()) + ":" + signature + // Get the current Unix timestamp as a string. + now := strconv.Itoa(int(time.Now().Unix())) + + // Prepare the data to be signed. + data := "\x19Ethereum Signed Message:\n" + strconv.Itoa(len(now)) + now + + sig, err := reqSigner.SignMessage(ctx, []byte(data), true) + + if err != nil { + return "", fmt.Errorf("failed to sign request: %w", err) + } + + return fmt.Sprintf("%s:%s", now, hexutil.Encode(signer.Encode(sig))), nil +} + // NewUnauthenticatedClient creates a new client for the RFQ quoting API. func NewUnauthenticatedClient(metricHandler metrics.Handler, rfqURL string) (UnauthenticatedClient, error) { client := resty.New(). @@ -115,7 +134,7 @@ func NewUnauthenticatedClient(metricHandler metrics.Handler, rfqURL string) (Una } // PutQuote puts a new quote in the RFQ quoting API. -func (c *clientImpl) PutQuote(ctx context.Context, q *model.PutQuoteRequest) error { +func (c *clientImpl) PutQuote(ctx context.Context, q *model.PutRelayerQuoteRequest) error { res, err := c.rClient.R(). SetContext(ctx). SetBody(q). @@ -159,6 +178,171 @@ func (c *clientImpl) PutRelayAck(ctx context.Context, req *model.PutAckRequest) return ack, nil } +func (c *clientImpl) SubscribeActiveQuotes(ctx context.Context, req *model.SubscribeActiveRFQRequest, reqChan chan *model.ActiveRFQMessage) (respChan chan *model.ActiveRFQMessage, err error) { + conn, err := c.connectWebsocket(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to connect to websocket: %w", err) + } + // first, subscrbe to the given chains + sub := model.SubscriptionParams{ + Chains: req.ChainIDs, + } + subJSON, err := json.Marshal(sub) + if err != nil { + return respChan, fmt.Errorf("error marshaling subscription params: %w", err) + } + err = conn.WriteJSON(model.ActiveRFQMessage{ + Op: rest.SubscribeOp, + Content: json.RawMessage(subJSON), + }) + if err != nil { + return nil, fmt.Errorf("error sending subscribe message: %w", err) + } + // make sure subscription is successful + var resp model.ActiveRFQMessage + err = conn.ReadJSON(&resp) + if err != nil { + return nil, fmt.Errorf("error reading subscribe response: %w", err) + } + if !resp.Success || resp.Op != rest.SubscribeOp { + return nil, fmt.Errorf("subscription failed") + } + + respChan = make(chan *model.ActiveRFQMessage) + go func() { + wsErr := c.processWebsocket(ctx, conn, reqChan, respChan) + if wsErr != nil { + logger.Error("Error running websocket listener: %s", wsErr) + } + }() + + return respChan, nil +} + +func (c *clientImpl) connectWebsocket(ctx context.Context, req *model.SubscribeActiveRFQRequest) (conn *websocket.Conn, err error) { + if len(req.ChainIDs) == 0 { + return nil, fmt.Errorf("chain IDs are required") + } + + header, err := c.getWsHeaders(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to get auth header: %w", err) + } + + reqURL := strings.Replace(c.rClient.BaseURL, "http", "ws", 1) + rest.RFQStreamRoute + conn, httpResp, err := websocket.DefaultDialer.Dial(reqURL, header) + if err != nil { + return nil, fmt.Errorf("failed to connect to websocket: %w", err) + } + err = httpResp.Body.Close() + if err != nil { + logger.Warnf("error closing websocket connection: %v", err) + } + + return conn, nil +} + +func (c *clientImpl) getWsHeaders(ctx context.Context, req *model.SubscribeActiveRFQRequest) (header http.Header, err error) { + header = http.Header{} + chainIDsJSON, err := json.Marshal(req.ChainIDs) + if err != nil { + return header, fmt.Errorf("failed to marshal chain IDs: %w", err) + } + header.Set(rest.ChainsHeader, string(chainIDsJSON)) + authHeader, err := getAuthHeader(ctx, c.reqSigner) + if err != nil { + return header, fmt.Errorf("failed to get auth header: %w", err) + } + header.Set(rest.AuthorizationHeader, authHeader) + return header, nil +} + +func (c *clientImpl) processWebsocket(ctx context.Context, conn *websocket.Conn, reqChan, respChan chan *model.ActiveRFQMessage) (err error) { + defer func() { + close(respChan) + err := conn.Close() + if err != nil { + logger.Warnf("error closing websocket connection: %v", err) + } + }() + + readChan := make(chan []byte) + go c.listenWsMessages(ctx, conn, readChan) + go c.sendPings(ctx, reqChan) + + for { + select { + case <-ctx.Done(): + return nil + case msg, ok := <-reqChan: + if !ok { + return fmt.Errorf("error reading from reqChan: %w", ctx.Err()) + } + err := conn.WriteJSON(msg) + if err != nil { + return fmt.Errorf("error sending message to websocket: %w", err) + } + case msg, ok := <-readChan: + if !ok { + return nil + } + err = c.handleWsMessage(ctx, msg, respChan) + if err != nil { + return fmt.Errorf("error handling websocket message: %w", err) + } + } + } +} + +func (c *clientImpl) sendPings(ctx context.Context, reqChan chan *model.ActiveRFQMessage) { + pingTicker := time.NewTicker(pingPeriod) + defer pingTicker.Stop() + + for { + select { + case <-pingTicker.C: + pingMsg := model.ActiveRFQMessage{ + Op: rest.PingOp, + } + reqChan <- &pingMsg + case <-ctx.Done(): + return + } + } +} +func (c *clientImpl) listenWsMessages(ctx context.Context, conn *websocket.Conn, readChan chan []byte) { + defer close(readChan) + for { + _, message, err := conn.ReadMessage() + if err != nil { + if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { + logger.Warnf("websocket connection closed unexpectedly: %v", err) + } + return + } + select { + case readChan <- message: + case <-ctx.Done(): + return + } + } +} + +func (c *clientImpl) handleWsMessage(ctx context.Context, msg []byte, respChan chan *model.ActiveRFQMessage) (err error) { + var rfqMsg model.ActiveRFQMessage + err = json.Unmarshal(msg, &rfqMsg) + if err != nil { + return fmt.Errorf("error unmarshaling message: %w", err) + } + + select { + case respChan <- &rfqMsg: + case <-ctx.Done(): + return nil + } + return nil +} + // GetAllQuotes retrieves all quotes from the RFQ quoting API. func (c *unauthenticatedClient) GetAllQuotes(ctx context.Context) ([]*model.GetQuoteResponse, error) { var quotes []*model.GetQuoteResponse @@ -242,6 +426,25 @@ func (c unauthenticatedClient) GetRFQContracts(ctx context.Context) (*model.GetC return contracts, nil } +func (c unauthenticatedClient) PutRFQRequest(ctx context.Context, q *model.PutRFQRequest) (*model.PutRFQResponse, error) { + var response model.PutRFQResponse + resp, err := c.rClient.R(). + SetContext(ctx). + SetBody(q). + SetResult(&response). + Put(rest.RFQRoute) + + if err != nil { + return nil, fmt.Errorf("error from server: %s: %w", getStatus(resp), err) + } + + if resp.IsError() { + return nil, fmt.Errorf("error from server: %s", getStatus(resp)) + } + + return &response, nil +} + func getStatus(resp *resty.Response) string { if resp == nil { return "http status unavailable" diff --git a/services/rfq/api/client/client_test.go b/services/rfq/api/client/client_test.go index bfc8dc3483..0314f9018f 100644 --- a/services/rfq/api/client/client_test.go +++ b/services/rfq/api/client/client_test.go @@ -7,7 +7,7 @@ import ( // TODO: @aurelius tese tests make a lot less sesnes w/ a composite index func (c *ClientSuite) TestPutAndGetQuote() { - req := model.PutQuoteRequest{ + req := model.PutRelayerQuoteRequest{ OriginChainID: 1, OriginTokenAddr: "0xOriginTokenAddr", DestChainID: 42161, @@ -40,7 +40,7 @@ func (c *ClientSuite) TestPutAndGetQuote() { func (c *ClientSuite) TestPutAndGetBulkQuotes() { req := model.PutBulkQuotesRequest{ - Quotes: []model.PutQuoteRequest{ + Quotes: []model.PutRelayerQuoteRequest{ { OriginChainID: 1, OriginTokenAddr: "0xOriginTokenAddr", @@ -98,7 +98,7 @@ func (c *ClientSuite) TestPutAndGetBulkQuotes() { } func (c *ClientSuite) TestGetSpecificQuote() { - req := model.PutQuoteRequest{ + req := model.PutRelayerQuoteRequest{ OriginChainID: 1, OriginTokenAddr: "0xOriginTokenAddr", DestChainID: 42161, @@ -135,7 +135,7 @@ func (c *ClientSuite) TestGetSpecificQuote() { } func (c *ClientSuite) TestGetQuoteByRelayerAddress() { - req := model.PutQuoteRequest{ + req := model.PutRelayerQuoteRequest{ OriginChainID: 1, OriginTokenAddr: "0xOriginTokenAddr", DestChainID: 42161, diff --git a/services/rfq/api/db/activequoterequeststatus_string.go b/services/rfq/api/db/activequoterequeststatus_string.go new file mode 100644 index 0000000000..cb9e64a4d6 --- /dev/null +++ b/services/rfq/api/db/activequoterequeststatus_string.go @@ -0,0 +1,27 @@ +// Code generated by "stringer -type=ActiveQuoteRequestStatus"; DO NOT EDIT. + +package db + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Received-1] + _ = x[Pending-2] + _ = x[Expired-3] + _ = x[Closed-4] +} + +const _ActiveQuoteRequestStatus_name = "ReceivedPendingExpiredClosed" + +var _ActiveQuoteRequestStatus_index = [...]uint8{0, 8, 15, 22, 31} + +func (i ActiveQuoteRequestStatus) String() string { + i -= 1 + if i >= ActiveQuoteRequestStatus(len(_ActiveQuoteRequestStatus_index)-1) { + return "ActiveQuoteRequestStatus(" + strconv.FormatInt(int64(i+1), 10) + ")" + } + return _ActiveQuoteRequestStatus_name[_ActiveQuoteRequestStatus_index[i]:_ActiveQuoteRequestStatus_index[i+1]] +} diff --git a/services/rfq/api/db/activequoteresponsestatus_string.go b/services/rfq/api/db/activequoteresponsestatus_string.go new file mode 100644 index 0000000000..564f93f4c1 --- /dev/null +++ b/services/rfq/api/db/activequoteresponsestatus_string.go @@ -0,0 +1,28 @@ +// Code generated by "stringer -type=ActiveQuoteResponseStatus"; DO NOT EDIT. + +package db + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Considered-1] + _ = x[Returned-2] + _ = x[PastExpiration-3] + _ = x[Malformed-4] + _ = x[Duplicate-5] +} + +const _ActiveQuoteResponseStatus_name = "ConsideredReturnedPastExpirationMalformedDuplicate" + +var _ActiveQuoteResponseStatus_index = [...]uint8{0, 10, 18, 32, 41, 50} + +func (i ActiveQuoteResponseStatus) String() string { + i -= 1 + if i >= ActiveQuoteResponseStatus(len(_ActiveQuoteResponseStatus_index)-1) { + return "ActiveQuoteResponseStatus(" + strconv.FormatInt(int64(i+1), 10) + ")" + } + return _ActiveQuoteResponseStatus_name[_ActiveQuoteResponseStatus_index[i]:_ActiveQuoteResponseStatus_index[i+1]] +} diff --git a/services/rfq/api/db/api_db.go b/services/rfq/api/db/api_db.go index 48c7344484..44d9fb0a25 100644 --- a/services/rfq/api/db/api_db.go +++ b/services/rfq/api/db/api_db.go @@ -3,9 +3,13 @@ package db import ( "context" + "database/sql/driver" + "fmt" "time" "github.com/shopspring/decimal" + "github.com/synapsecns/sanguine/core/dbcommon" + "github.com/synapsecns/sanguine/services/rfq/api/model" ) // Quote is the database model for a quote. @@ -34,6 +38,172 @@ type Quote struct { UpdatedAt time.Time } +// ActiveQuoteRequestStatus is the status of a quote request in the db. +// This is the primary mechanism for moving data through the app. +// +// TODO: consider making this an interface and exporting that. +// +// EXTREMELY IMPORTANT: DO NOT ADD NEW VALUES TO THIS ENUM UNLESS THEY ARE AT THE END. +// +//go:generate go run golang.org/x/tools/cmd/stringer -type=ActiveQuoteRequestStatus +type ActiveQuoteRequestStatus uint8 + +const ( + // Received means the quote request has been received by the server. + Received ActiveQuoteRequestStatus = iota + 1 + // Pending means the quote request is pending awaiting relayer responses. + Pending + // Expired means the quote request has expired without any valid responses. + Expired + // Closed means the quote request has been fulfilled. + Closed +) + +// Int returns the int value of the quote request status. +func (q ActiveQuoteRequestStatus) Int() uint8 { + return uint8(q) +} + +// GormDataType implements the gorm common interface for enums. +func (q ActiveQuoteRequestStatus) GormDataType() string { + return dbcommon.EnumDataType +} + +// Scan implements the gorm common interface for enums. +func (q *ActiveQuoteRequestStatus) Scan(src any) error { + res, err := dbcommon.EnumScan(src) + if err != nil { + return fmt.Errorf("could not scan %w", err) + } + newStatus := ActiveQuoteRequestStatus(res) + *q = newStatus + return nil +} + +// Value implements the gorm common interface for enums. +func (q ActiveQuoteRequestStatus) Value() (driver.Value, error) { + // nolint: wrapcheck + return dbcommon.EnumValue(q) +} + +var _ dbcommon.Enum = (*ActiveQuoteRequestStatus)(nil) + +// ActiveQuoteResponseStatus is the status of a quote request in the db. +// This is the primary mechanism for moving data through the app. +// +// TODO: consider making this an interface and exporting that. +// +// EXTREMELY IMPORTANT: DO NOT ADD NEW VALUES TO THIS ENUM UNLESS THEY ARE AT THE END. +// +//go:generate go run golang.org/x/tools/cmd/stringer -type=ActiveQuoteResponseStatus +type ActiveQuoteResponseStatus uint8 + +const ( + // Considered means the quote request was considered by the relayer, but was not ultimately the fulfilling response. + Considered ActiveQuoteResponseStatus = iota + 1 + // Returned means the quote request was returned by the relayer to the user. + Returned + // PastExpiration means the quote request was received, but past the expiration window. + PastExpiration + // Malformed means that the quote request was malformed. + Malformed + // Duplicate means that the quote request was a duplicate. + Duplicate +) + +// Int returns the int value of the quote request status. +func (q ActiveQuoteResponseStatus) Int() uint8 { + return uint8(q) +} + +// GormDataType implements the gorm common interface for enums. +func (q ActiveQuoteResponseStatus) GormDataType() string { + return dbcommon.EnumDataType +} + +// Scan implements the gorm common interface for enums. +func (q *ActiveQuoteResponseStatus) Scan(src any) error { + res, err := dbcommon.EnumScan(src) + if err != nil { + return fmt.Errorf("could not scan %w", err) + } + newStatus := ActiveQuoteResponseStatus(res) + *q = newStatus + return nil +} + +// Value implements the gorm common interface for enums. +func (q ActiveQuoteResponseStatus) Value() (driver.Value, error) { + // nolint: wrapcheck + return dbcommon.EnumValue(q) +} + +var _ dbcommon.Enum = (*ActiveQuoteResponseStatus)(nil) + +// ActiveQuoteRequest is the database model for an active quote request. +type ActiveQuoteRequest struct { + RequestID string `gorm:"column:request_id;primaryKey"` + IntegratorID string `gorm:"column:integrator_id"` + UserAddress string `gorm:"column:user_address"` + OriginChainID uint64 `gorm:"column:origin_chain_id"` + OriginTokenAddr string `gorm:"column:origin_token"` + DestChainID uint64 `gorm:"column:dest_chain_id"` + DestTokenAddr string `gorm:"column:dest_token"` + OriginAmount decimal.Decimal `gorm:"column:origin_amount"` + ExpirationWindow time.Duration `gorm:"column:expiration_window"` + CreatedAt time.Time `gorm:"column:created_at"` + Status ActiveQuoteRequestStatus `gorm:"column:status"` + ClosedAt *time.Time `gorm:"column:closed_at"` + ClosedQuoteID *string `gorm:"column:closed_quote_id"` +} + +// FromUserRequest converts a model.PutRFQRequest to an ActiveQuoteRequest. +func FromUserRequest(req *model.PutRFQRequest, requestID string) (*ActiveQuoteRequest, error) { + originAmount, err := decimal.NewFromString(req.Data.OriginAmount) + if err != nil { + return nil, fmt.Errorf("invalid origin amount: %w", err) + } + return &ActiveQuoteRequest{ + RequestID: requestID, + IntegratorID: req.IntegratorID, + UserAddress: req.UserAddress, + OriginChainID: uint64(req.Data.OriginChainID), + OriginTokenAddr: req.Data.OriginTokenAddr, + DestChainID: uint64(req.Data.DestChainID), + DestTokenAddr: req.Data.DestTokenAddr, + OriginAmount: originAmount, + ExpirationWindow: time.Duration(req.Data.ExpirationWindow), + CreatedAt: time.Now(), + Status: Received, + }, nil +} + +// ActiveQuoteResponse is the database model for an active quote response. +type ActiveQuoteResponse struct { + RequestID string `gorm:"column:request_id"` + QuoteID string `gorm:"column:quote_id;primaryKey"` + DestAmount decimal.Decimal `gorm:"column:dest_amount"` + RelayerAddr string `gorm:"column:relayer_address"` + UpdatedAt time.Time `gorm:"column:updated_at"` + Status ActiveQuoteResponseStatus `gorm:"column:status"` +} + +// FromRelayerResponse converts a model.WsRFQResponse to an ActiveQuoteResponse. +func FromRelayerResponse(resp *model.WsRFQResponse, relayerAddr string, status ActiveQuoteResponseStatus) (*ActiveQuoteResponse, error) { + destAmount, err := decimal.NewFromString(resp.DestAmount) + if err != nil { + return nil, fmt.Errorf("invalid dest amount: %w", err) + } + return &ActiveQuoteResponse{ + RequestID: resp.RequestID, + QuoteID: resp.QuoteID, + DestAmount: destAmount, + RelayerAddr: relayerAddr, + UpdatedAt: resp.UpdatedAt, + Status: status, + }, nil +} + // APIDBReader is the interface for reading from the database. type APIDBReader interface { // GetQuotesByDestChainAndToken gets quotes from the database by destination chain and token. @@ -42,6 +212,8 @@ type APIDBReader interface { GetQuotesByOriginAndDestination(ctx context.Context, originChainID uint64, originTokenAddr string, destChainID uint64, destTokenAddr string) ([]*Quote, error) // GetQuotesByRelayerAddress gets quotes from the database by relayer address. GetQuotesByRelayerAddress(ctx context.Context, relayerAddress string) ([]*Quote, error) + // GetActiveQuoteRequests gets active quote requests from the database. + GetActiveQuoteRequests(ctx context.Context, matchStatuses ...ActiveQuoteRequestStatus) ([]*ActiveQuoteRequest, error) // GetAllQuotes retrieves all quotes from the database. GetAllQuotes(ctx context.Context) ([]*Quote, error) } @@ -52,6 +224,14 @@ type APIDBWriter interface { UpsertQuote(ctx context.Context, quote *Quote) error // UpsertQuotes upserts multiple quotes in the database. UpsertQuotes(ctx context.Context, quotes []*Quote) error + // InsertActiveQuoteRequest inserts an active quote request into the database. + InsertActiveQuoteRequest(ctx context.Context, req *model.PutRFQRequest, requestID string) error + // UpdateActiveQuoteRequestStatus updates the status of an active quote request in the database. + UpdateActiveQuoteRequestStatus(ctx context.Context, requestID string, quoteID *string, status ActiveQuoteRequestStatus) error + // InsertActiveQuoteResponse inserts an active quote response into the database. + InsertActiveQuoteResponse(ctx context.Context, resp *model.WsRFQResponse, relayerAddr string, status ActiveQuoteResponseStatus) error + // UpdateActiveQuoteResponseStatus updates the status of an active quote response in the database. + UpdateActiveQuoteResponseStatus(ctx context.Context, quoteID string, status ActiveQuoteResponseStatus) error } // APIDB is the interface for the database service. diff --git a/services/rfq/api/db/sql/base/base.go b/services/rfq/api/db/sql/base/base.go index 34eddbc5ca..a2cca36a78 100644 --- a/services/rfq/api/db/sql/base/base.go +++ b/services/rfq/api/db/sql/base/base.go @@ -25,7 +25,7 @@ func (s Store) DB() *gorm.DB { // GetAllModels gets all models to migrate. // see: https://medium.com/@SaifAbid/slice-interfaces-8c78f8b6345d for an explanation of why we can't do this at initialization time func GetAllModels() (allModels []interface{}) { - allModels = append(allModels, &db.Quote{}) + allModels = append(allModels, &db.Quote{}, &db.ActiveQuoteRequest{}, &db.ActiveQuoteResponse{}) return allModels } diff --git a/services/rfq/api/db/sql/base/store.go b/services/rfq/api/db/sql/base/store.go index 8ef37607e8..312ec15472 100644 --- a/services/rfq/api/db/sql/base/store.go +++ b/services/rfq/api/db/sql/base/store.go @@ -3,10 +3,12 @@ package base import ( "context" "fmt" + "time" "gorm.io/gorm/clause" "github.com/synapsecns/sanguine/services/rfq/api/db" + "github.com/synapsecns/sanguine/services/rfq/api/model" ) // GetQuotesByDestChainAndToken gets quotes from the database by destination chain and token. @@ -77,3 +79,78 @@ func (s *Store) UpsertQuotes(ctx context.Context, quotes []*db.Quote) error { } return nil } + +// InsertActiveQuoteRequest inserts an active quote request into the database. +func (s *Store) InsertActiveQuoteRequest(ctx context.Context, req *model.PutRFQRequest, requestID string) error { + dbReq, err := db.FromUserRequest(req, requestID) + if err != nil { + return fmt.Errorf("could not convert user request to database request: %w", err) + } + result := s.db.WithContext(ctx).Create(dbReq) + if result.Error != nil { + return fmt.Errorf("could not insert active quote request: %w", result.Error) + } + return nil +} + +// UpdateActiveQuoteRequestStatus updates the status of an active quote request in the database. +func (s *Store) UpdateActiveQuoteRequestStatus(ctx context.Context, requestID string, quoteID *string, status db.ActiveQuoteRequestStatus) error { + updates := map[string]interface{}{ + "status": status, + } + if status == db.Closed { + if quoteID == nil { + return fmt.Errorf("quote id is required for fulfilled status") + } + updates["closed_quote_id"] = quoteID + updates["closed_at"] = time.Now().UTC() + } + result := s.db.WithContext(ctx). + Model(&db.ActiveQuoteRequest{}). + Where("request_id = ?", requestID). + Updates(updates) + if result.Error != nil { + return fmt.Errorf("could not update active quote request status: %w", result.Error) + } + return nil +} + +// InsertActiveQuoteResponse inserts an active quote response into the database. +func (s *Store) InsertActiveQuoteResponse(ctx context.Context, resp *model.WsRFQResponse, relayerAddr string, status db.ActiveQuoteResponseStatus) error { + dbReq, err := db.FromRelayerResponse(resp, relayerAddr, status) + if err != nil { + return fmt.Errorf("could not convert relayer response to database response: %w", err) + } + result := s.db.WithContext(ctx).Create(dbReq) + if result.Error != nil { + return fmt.Errorf("could not insert active quote response: %w", result.Error) + } + return nil +} + +// UpdateActiveQuoteResponseStatus updates the status of an active quote response in the database. +func (s *Store) UpdateActiveQuoteResponseStatus(ctx context.Context, quoteID string, status db.ActiveQuoteResponseStatus) error { + result := s.db.WithContext(ctx). + Model(&db.ActiveQuoteResponse{}). + Where("quote_id = ?", quoteID). + Update("status", status) + if result.Error != nil { + return fmt.Errorf("could not update active quote response status: %w", result.Error) + } + return nil +} + +// GetActiveQuoteRequests gets active quote requests from the database. +func (s *Store) GetActiveQuoteRequests(ctx context.Context, matchStatuses ...db.ActiveQuoteRequestStatus) ([]*db.ActiveQuoteRequest, error) { + var requests []*db.ActiveQuoteRequest + + query := s.db.WithContext(ctx).Model(&db.ActiveQuoteRequest{}) + if len(matchStatuses) > 0 { + query = query.Where("status IN ?", matchStatuses) + } + result := query.Find(&requests) + if result.Error != nil { + return nil, result.Error + } + return requests, nil +} diff --git a/services/rfq/api/docs/docs.go b/services/rfq/api/docs/docs.go index af06bfa449..447b332d29 100644 --- a/services/rfq/api/docs/docs.go +++ b/services/rfq/api/docs/docs.go @@ -35,7 +35,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.PutQuoteRequest" + "$ref": "#/definitions/model.PutRelayerQuoteRequest" } } ], @@ -121,6 +121,38 @@ const docTemplate = `{ } } }, + "/open_quote_requests": { + "get": { + "description": "Get all open quote requests that are currently in Received or Pending status.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "quotes" + ], + "summary": "Get open quote requests", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.GetOpenQuoteRequestsResponse" + } + }, + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } + } + } + } + } + }, "/quotes": { "get": { "description": "get quotes from all relayers.", @@ -203,7 +235,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.PutQuoteRequest" + "$ref": "#/definitions/model.PutRelayerQuoteRequest" } } ], @@ -219,6 +251,72 @@ const docTemplate = `{ } } } + }, + "/rfq": { + "put": { + "description": "Handle user quote request and return the best quote available.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "quotes" + ], + "summary": "Handle user quote request", + "parameters": [ + { + "description": "User quote request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.PutRFQRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.PutRFQResponse" + }, + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } + } + } + } + } + }, + "/rfq_stream": { + "get": { + "description": "Establish a WebSocket connection to receive active quote requests.", + "produces": [ + "application/json" + ], + "tags": [ + "quotes" + ], + "summary": "Handle WebSocket connection for active quote requests", + "responses": { + "101": { + "description": "Switching Protocols", + "schema": { + "type": "string" + }, + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } + } + } + } + } } }, "definitions": { @@ -234,6 +332,35 @@ const docTemplate = `{ } } }, + "model.GetOpenQuoteRequestsResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "dest_chain_id": { + "type": "integer" + }, + "dest_token": { + "type": "string" + }, + "expiration_window": { + "type": "integer" + }, + "origin_amount": { + "type": "string" + }, + "origin_chain_id": { + "type": "integer" + }, + "origin_token": { + "type": "string" + }, + "user_address": { + "type": "string" + } + } + }, "model.GetQuoteResponse": { "type": "object", "properties": { @@ -289,12 +416,55 @@ const docTemplate = `{ "quotes": { "type": "array", "items": { - "$ref": "#/definitions/model.PutQuoteRequest" + "$ref": "#/definitions/model.PutRelayerQuoteRequest" + } + } + } + }, + "model.PutRFQRequest": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.QuoteData" + }, + "integrator_id": { + "type": "string" + }, + "quote_types": { + "type": "array", + "items": { + "type": "string" } + }, + "user_address": { + "type": "string" + } + } + }, + "model.PutRFQResponse": { + "type": "object", + "properties": { + "dest_amount": { + "type": "string" + }, + "quote_id": { + "type": "string" + }, + "quote_type": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "relayer_address": { + "type": "string" + }, + "success": { + "type": "boolean" } } }, - "model.PutQuoteRequest": { + "model.PutRelayerQuoteRequest": { "type": "object", "properties": { "dest_amount": { @@ -325,6 +495,38 @@ const docTemplate = `{ "type": "string" } } + }, + "model.QuoteData": { + "type": "object", + "properties": { + "dest_amount": { + "type": "string" + }, + "dest_chain_id": { + "type": "integer" + }, + "dest_token_addr": { + "type": "string" + }, + "expiration_window": { + "type": "integer" + }, + "origin_amount": { + "type": "string" + }, + "origin_chain_id": { + "type": "integer" + }, + "origin_token_addr": { + "type": "string" + }, + "quote_id": { + "type": "string" + }, + "relayer_address": { + "type": "string" + } + } } } }` diff --git a/services/rfq/api/docs/swagger.json b/services/rfq/api/docs/swagger.json index 5d28bbf785..0357cd7e31 100644 --- a/services/rfq/api/docs/swagger.json +++ b/services/rfq/api/docs/swagger.json @@ -24,7 +24,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.PutQuoteRequest" + "$ref": "#/definitions/model.PutRelayerQuoteRequest" } } ], @@ -110,6 +110,38 @@ } } }, + "/open_quote_requests": { + "get": { + "description": "Get all open quote requests that are currently in Received or Pending status.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "quotes" + ], + "summary": "Get open quote requests", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.GetOpenQuoteRequestsResponse" + } + }, + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } + } + } + } + } + }, "/quotes": { "get": { "description": "get quotes from all relayers.", @@ -192,7 +224,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.PutQuoteRequest" + "$ref": "#/definitions/model.PutRelayerQuoteRequest" } } ], @@ -208,6 +240,72 @@ } } } + }, + "/rfq": { + "put": { + "description": "Handle user quote request and return the best quote available.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "quotes" + ], + "summary": "Handle user quote request", + "parameters": [ + { + "description": "User quote request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.PutRFQRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.PutRFQResponse" + }, + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } + } + } + } + } + }, + "/rfq_stream": { + "get": { + "description": "Establish a WebSocket connection to receive active quote requests.", + "produces": [ + "application/json" + ], + "tags": [ + "quotes" + ], + "summary": "Handle WebSocket connection for active quote requests", + "responses": { + "101": { + "description": "Switching Protocols", + "schema": { + "type": "string" + }, + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } + } + } + } + } } }, "definitions": { @@ -223,6 +321,35 @@ } } }, + "model.GetOpenQuoteRequestsResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "dest_chain_id": { + "type": "integer" + }, + "dest_token": { + "type": "string" + }, + "expiration_window": { + "type": "integer" + }, + "origin_amount": { + "type": "string" + }, + "origin_chain_id": { + "type": "integer" + }, + "origin_token": { + "type": "string" + }, + "user_address": { + "type": "string" + } + } + }, "model.GetQuoteResponse": { "type": "object", "properties": { @@ -278,12 +405,55 @@ "quotes": { "type": "array", "items": { - "$ref": "#/definitions/model.PutQuoteRequest" + "$ref": "#/definitions/model.PutRelayerQuoteRequest" + } + } + } + }, + "model.PutRFQRequest": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.QuoteData" + }, + "integrator_id": { + "type": "string" + }, + "quote_types": { + "type": "array", + "items": { + "type": "string" } + }, + "user_address": { + "type": "string" + } + } + }, + "model.PutRFQResponse": { + "type": "object", + "properties": { + "dest_amount": { + "type": "string" + }, + "quote_id": { + "type": "string" + }, + "quote_type": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "relayer_address": { + "type": "string" + }, + "success": { + "type": "boolean" } } }, - "model.PutQuoteRequest": { + "model.PutRelayerQuoteRequest": { "type": "object", "properties": { "dest_amount": { @@ -314,6 +484,38 @@ "type": "string" } } + }, + "model.QuoteData": { + "type": "object", + "properties": { + "dest_amount": { + "type": "string" + }, + "dest_chain_id": { + "type": "integer" + }, + "dest_token_addr": { + "type": "string" + }, + "expiration_window": { + "type": "integer" + }, + "origin_amount": { + "type": "string" + }, + "origin_chain_id": { + "type": "integer" + }, + "origin_token_addr": { + "type": "string" + }, + "quote_id": { + "type": "string" + }, + "relayer_address": { + "type": "string" + } + } } } } \ No newline at end of file diff --git a/services/rfq/api/docs/swagger.yaml b/services/rfq/api/docs/swagger.yaml index a8ddffdcc6..8e6bc56240 100644 --- a/services/rfq/api/docs/swagger.yaml +++ b/services/rfq/api/docs/swagger.yaml @@ -7,6 +7,25 @@ definitions: description: Contracts is a map of chain id to contract address type: object type: object + model.GetOpenQuoteRequestsResponse: + properties: + created_at: + type: string + dest_chain_id: + type: integer + dest_token: + type: string + expiration_window: + type: integer + origin_amount: + type: string + origin_chain_id: + type: integer + origin_token: + type: string + user_address: + type: string + type: object model.GetQuoteResponse: properties: dest_amount: @@ -55,10 +74,38 @@ definitions: properties: quotes: items: - $ref: '#/definitions/model.PutQuoteRequest' + $ref: '#/definitions/model.PutRelayerQuoteRequest' type: array type: object - model.PutQuoteRequest: + model.PutRFQRequest: + properties: + data: + $ref: '#/definitions/model.QuoteData' + integrator_id: + type: string + quote_types: + items: + type: string + type: array + user_address: + type: string + type: object + model.PutRFQResponse: + properties: + dest_amount: + type: string + quote_id: + type: string + quote_type: + type: string + reason: + type: string + relayer_address: + type: string + success: + type: boolean + type: object + model.PutRelayerQuoteRequest: properties: dest_amount: type: string @@ -79,6 +126,27 @@ definitions: origin_token_addr: type: string type: object + model.QuoteData: + properties: + dest_amount: + type: string + dest_chain_id: + type: integer + dest_token_addr: + type: string + expiration_window: + type: integer + origin_amount: + type: string + origin_chain_id: + type: integer + origin_token_addr: + type: string + quote_id: + type: string + relayer_address: + type: string + type: object info: contact: {} paths: @@ -93,7 +161,7 @@ paths: name: request required: true schema: - $ref: '#/definitions/model.PutQuoteRequest' + $ref: '#/definitions/model.PutRelayerQuoteRequest' produces: - application/json responses: @@ -151,6 +219,28 @@ paths: summary: Get contract addresses tags: - quotes + /open_quote_requests: + get: + consumes: + - application/json + description: Get all open quote requests that are currently in Received or Pending + status. + produces: + - application/json + responses: + "200": + description: OK + headers: + X-Api-Version: + description: API Version Number - See docs for more info + type: string + schema: + items: + $ref: '#/definitions/model.GetOpenQuoteRequestsResponse' + type: array + summary: Get open quote requests + tags: + - quotes /quotes: get: consumes: @@ -203,7 +293,7 @@ paths: name: request required: true schema: - $ref: '#/definitions/model.PutQuoteRequest' + $ref: '#/definitions/model.PutRelayerQuoteRequest' produces: - application/json responses: @@ -216,4 +306,47 @@ paths: summary: Upsert quote tags: - quotes + /rfq: + put: + consumes: + - application/json + description: Handle user quote request and return the best quote available. + parameters: + - description: User quote request + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.PutRFQRequest' + produces: + - application/json + responses: + "200": + description: OK + headers: + X-Api-Version: + description: API Version Number - See docs for more info + type: string + schema: + $ref: '#/definitions/model.PutRFQResponse' + summary: Handle user quote request + tags: + - quotes + /rfq_stream: + get: + description: Establish a WebSocket connection to receive active quote requests. + produces: + - application/json + responses: + "101": + description: Switching Protocols + headers: + X-Api-Version: + description: API Version Number - See docs for more info + type: string + schema: + type: string + summary: Handle WebSocket connection for active quote requests + tags: + - quotes swagger: "2.0" diff --git a/services/rfq/api/model/request.go b/services/rfq/api/model/request.go index cff2db2161..c0dd864068 100644 --- a/services/rfq/api/model/request.go +++ b/services/rfq/api/model/request.go @@ -1,7 +1,9 @@ package model -// PutQuoteRequest contains the schema for a PUT /quote request. -type PutQuoteRequest struct { +import "time" + +// PutRelayerQuoteRequest contains the schema for a PUT /quote request. +type PutRelayerQuoteRequest struct { OriginChainID int `json:"origin_chain_id"` OriginTokenAddr string `json:"origin_token_addr"` DestChainID int `json:"dest_chain_id"` @@ -15,7 +17,7 @@ type PutQuoteRequest struct { // PutBulkQuotesRequest contains the schema for a PUT /quote request. type PutBulkQuotesRequest struct { - Quotes []PutQuoteRequest `json:"quotes"` + Quotes []PutRelayerQuoteRequest `json:"quotes"` } // PutAckRequest contains the schema for a PUT /ack request. @@ -31,3 +33,54 @@ type GetQuoteSpecificRequest struct { DestChainID int `json:"destChainId"` DestTokenAddr string `json:"destTokenAddr"` } + +// PutRFQRequest represents a user request for quote. +type PutRFQRequest struct { + UserAddress string `json:"user_address"` + IntegratorID string `json:"integrator_id"` + QuoteTypes []string `json:"quote_types"` + Data QuoteData `json:"data"` +} + +// QuoteRequest represents a request for a quote. +type QuoteRequest struct { + RequestID string `json:"request_id"` + Data QuoteData `json:"data"` + CreatedAt time.Time `json:"created_at"` +} + +// QuoteData represents the data within a quote request. +type QuoteData struct { + OriginChainID int `json:"origin_chain_id"` + DestChainID int `json:"dest_chain_id"` + OriginTokenAddr string `json:"origin_token_addr"` + DestTokenAddr string `json:"dest_token_addr"` + OriginAmount string `json:"origin_amount"` + ExpirationWindow int64 `json:"expiration_window"` + DestAmount *string `json:"dest_amount"` + RelayerAddress *string `json:"relayer_address"` + QuoteID *string `json:"quote_id"` +} + +// WsRFQRequest represents a request for a quote to a relayer. +type WsRFQRequest struct { + RequestID string `json:"request_id"` + Data QuoteData `json:"data"` + CreatedAt time.Time `json:"created_at"` +} + +// SubscribeActiveRFQRequest represents a request to subscribe to active quotes. +// Note that this request is not actually bound to the request body, but rather the chain IDs +// are encoded under the ChainsHeader. +type SubscribeActiveRFQRequest struct { + ChainIDs []int `json:"chain_ids"` +} + +// NewWsRFQRequest creates a new WsRFQRequest. +func NewWsRFQRequest(data QuoteData, requestID string) *WsRFQRequest { + return &WsRFQRequest{ + RequestID: requestID, + Data: data, + CreatedAt: time.Now(), + } +} diff --git a/services/rfq/api/model/response.go b/services/rfq/api/model/response.go index 6cfd2a1599..72c534e91f 100644 --- a/services/rfq/api/model/response.go +++ b/services/rfq/api/model/response.go @@ -1,5 +1,10 @@ package model +import ( + "encoding/json" + "time" +) + // GetQuoteResponse contains the schema for a GET /quote response. type GetQuoteResponse struct { // OriginChainID is the chain which the relayer is willing to relay from @@ -41,3 +46,45 @@ type GetContractsResponse struct { // Contracts is a map of chain id to contract address Contracts map[uint32]string `json:"contracts"` } + +// ActiveRFQMessage represents the general structure of WebSocket messages for Active RFQ. +type ActiveRFQMessage struct { + Op string `json:"op"` + Content json.RawMessage `json:"content,omitempty"` + Success bool `json:"success,omitempty"` +} + +// PutRFQResponse represents a response to a user quote request. +type PutRFQResponse struct { + Success bool `json:"success"` + Reason string `json:"reason,omitempty"` + QuoteType string `json:"quote_type,omitempty"` + QuoteID *string `json:"quote_id,omitempty"` + DestAmount string `json:"dest_amount,omitempty"` + RelayerAddress string `json:"relayer_address,omitempty"` +} + +// WsRFQResponse represents a response to a quote request. +type WsRFQResponse struct { + RequestID string `json:"request_id"` + QuoteID string `json:"quote_id,omitempty"` + DestAmount string `json:"dest_amount"` + UpdatedAt time.Time `json:"updated_at"` +} + +// SubscriptionParams are the parameters for a subscription. +type SubscriptionParams struct { + Chains []int `json:"chains"` +} + +// GetOpenQuoteRequestsResponse represents a response to a GET /open_quote_requests request. +type GetOpenQuoteRequestsResponse struct { + UserAddress string `json:"user_address"` + OriginChainID uint64 `json:"origin_chain_id"` + OriginTokenAddr string `json:"origin_token"` + DestChainID uint64 `json:"dest_chain_id"` + DestTokenAddr string `json:"dest_token"` + OriginAmount string `json:"origin_amount"` + ExpirationWindow int `json:"expiration_window"` + CreatedAt time.Time `json:"created_at"` +} diff --git a/services/rfq/api/model/util.go b/services/rfq/api/model/util.go deleted file mode 100644 index 3f35f7b14f..0000000000 --- a/services/rfq/api/model/util.go +++ /dev/null @@ -1,24 +0,0 @@ -package model - -import ( - "time" - - "github.com/synapsecns/sanguine/services/rfq/api/db" -) - -// QuoteResponseFromDbQuote converts a db.Quote to a GetQuoteResponse. -func QuoteResponseFromDbQuote(dbQuote *db.Quote) *GetQuoteResponse { - return &GetQuoteResponse{ - OriginChainID: int(dbQuote.OriginChainID), - OriginTokenAddr: dbQuote.OriginTokenAddr, - DestChainID: int(dbQuote.DestChainID), - DestTokenAddr: dbQuote.DestTokenAddr, - DestAmount: dbQuote.DestAmount.String(), - MaxOriginAmount: dbQuote.MaxOriginAmount.String(), - FixedFee: dbQuote.FixedFee.String(), - RelayerAddr: dbQuote.RelayerAddr, - OriginFastBridgeAddress: dbQuote.OriginFastBridgeAddress, - DestFastBridgeAddress: dbQuote.DestFastBridgeAddress, - UpdatedAt: dbQuote.UpdatedAt.Format(time.RFC3339), - } -} diff --git a/services/rfq/api/rest/auth.go b/services/rfq/api/rest/auth.go index 0c407b8de3..e8d5f24bc6 100644 --- a/services/rfq/api/rest/auth.go +++ b/services/rfq/api/rest/auth.go @@ -20,7 +20,7 @@ import ( // so that full auth header string: auth = strconv.Itoa(time.Now().Unix()) + ":" + signature // see: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign func EIP191Auth(c *gin.Context, deadline int64) (accountRecovered common.Address, err error) { - auth := c.Request.Header.Get("Authorization") + auth := c.Request.Header.Get(AuthorizationHeader) // parse : s := strings.Split(auth, ":") diff --git a/services/rfq/api/rest/handler.go b/services/rfq/api/rest/handler.go index 2878fb4cb3..c0aa5910e8 100644 --- a/services/rfq/api/rest/handler.go +++ b/services/rfq/api/rest/handler.go @@ -44,7 +44,7 @@ func APIVersionMiddleware(serverVersion string) gin.HandlerFunc { // @Summary Upsert quote // @Schemes // @Description upsert a quote from relayer. -// @Param request body model.PutQuoteRequest true "query params" +// @Param request body model.PutRelayerQuoteRequest true "query params" // @Tags quotes // @Accept json // @Produce json @@ -63,7 +63,7 @@ func (h *Handler) ModifyQuote(c *gin.Context) { c.JSON(http.StatusBadRequest, gin.H{"error": "No relayer address recovered from signature"}) return } - putRequest, ok := req.(*model.PutQuoteRequest) + putRequest, ok := req.(*model.PutRelayerQuoteRequest) if !ok { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request type"}) return @@ -133,7 +133,8 @@ func (h *Handler) ModifyBulkQuotes(c *gin.Context) { c.Status(http.StatusOK) } -func parseDBQuote(putRequest model.PutQuoteRequest, relayerAddr interface{}) (*db.Quote, error) { +//nolint:gosec +func parseDBQuote(putRequest model.PutRelayerQuoteRequest, relayerAddr interface{}) (*db.Quote, error) { destAmount, err := decimal.NewFromString(putRequest.DestAmount) if err != nil { return nil, fmt.Errorf("invalid DestAmount") @@ -162,6 +163,23 @@ func parseDBQuote(putRequest model.PutQuoteRequest, relayerAddr interface{}) (*d }, nil } +//nolint:gosec +func quoteResponseFromDBQuote(dbQuote *db.Quote) *model.GetQuoteResponse { + return &model.GetQuoteResponse{ + OriginChainID: int(dbQuote.OriginChainID), + OriginTokenAddr: dbQuote.OriginTokenAddr, + DestChainID: int(dbQuote.DestChainID), + DestTokenAddr: dbQuote.DestTokenAddr, + DestAmount: dbQuote.DestAmount.String(), + MaxOriginAmount: dbQuote.MaxOriginAmount.String(), + FixedFee: dbQuote.FixedFee.String(), + RelayerAddr: dbQuote.RelayerAddr, + OriginFastBridgeAddress: dbQuote.OriginFastBridgeAddress, + DestFastBridgeAddress: dbQuote.DestFastBridgeAddress, + UpdatedAt: dbQuote.UpdatedAt.Format(time.RFC3339), + } +} + // GetQuotes retrieves all quotes from the database. // GET /quotes. // nolint: cyclop @@ -229,11 +247,48 @@ func (h *Handler) GetQuotes(c *gin.Context) { // Convert quotes from db model to api model quotes := make([]*model.GetQuoteResponse, len(dbQuotes)) for i, dbQuote := range dbQuotes { - quotes[i] = model.QuoteResponseFromDbQuote(dbQuote) + quotes[i] = quoteResponseFromDBQuote(dbQuote) + } + c.JSON(http.StatusOK, quotes) +} + +// GetOpenQuoteRequests retrieves all open quote requests. +// GET /open_quote_requests +// @Summary Get open quote requests +// @Description Get all open quote requests that are currently in Received or Pending status. +// @Tags quotes +// @Accept json +// @Produce json +// @Success 200 {array} model.GetOpenQuoteRequestsResponse +// @Header 200 {string} X-Api-Version "API Version Number - See docs for more info" +// @Router /open_quote_requests [get]. +func (h *Handler) GetOpenQuoteRequests(c *gin.Context) { + dbQuotes, err := h.db.GetActiveQuoteRequests(c, db.Received, db.Pending) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + quotes := make([]*model.GetOpenQuoteRequestsResponse, len(dbQuotes)) + for i, dbQuote := range dbQuotes { + quotes[i] = dbActiveQuoteRequestToModel(dbQuote) } c.JSON(http.StatusOK, quotes) } +func dbActiveQuoteRequestToModel(dbQuote *db.ActiveQuoteRequest) *model.GetOpenQuoteRequestsResponse { + return &model.GetOpenQuoteRequestsResponse{ + UserAddress: dbQuote.UserAddress, + OriginChainID: dbQuote.OriginChainID, + OriginTokenAddr: dbQuote.OriginTokenAddr, + DestChainID: dbQuote.DestChainID, + DestTokenAddr: dbQuote.DestTokenAddr, + OriginAmount: dbQuote.OriginAmount.String(), + ExpirationWindow: int(dbQuote.ExpirationWindow.Milliseconds()), + CreatedAt: dbQuote.CreatedAt, + } +} + // GetContracts retrieves all contracts api is currently enabled on. // GET /contracts. // PingExample godoc diff --git a/services/rfq/api/rest/pubsub.go b/services/rfq/api/rest/pubsub.go new file mode 100644 index 0000000000..b92987da17 --- /dev/null +++ b/services/rfq/api/rest/pubsub.go @@ -0,0 +1,80 @@ +package rest + +import ( + "fmt" + + "github.com/puzpuzpuz/xsync" + "github.com/synapsecns/sanguine/services/rfq/api/model" +) + +// PubSubManager is a manager for a pubsub system. +type PubSubManager interface { + AddSubscription(relayerAddr string, params model.SubscriptionParams) error + RemoveSubscription(relayerAddr string, params model.SubscriptionParams) error + IsSubscribed(relayerAddr string, origin, dest int) bool +} + +type pubSubManagerImpl struct { + subscriptions *xsync.MapOf[string, map[int]struct{}] +} + +// NewPubSubManager creates a new pubsub manager. +func NewPubSubManager() PubSubManager { + return &pubSubManagerImpl{ + subscriptions: xsync.NewMapOf[map[int]struct{}](), + } +} + +func (p *pubSubManagerImpl) AddSubscription(relayerAddr string, params model.SubscriptionParams) error { + if params.Chains == nil { + return fmt.Errorf("chains is nil") + } + + sub, ok := p.subscriptions.Load(relayerAddr) + if !ok { + sub = make(map[int]struct{}) + for _, c := range params.Chains { + sub[c] = struct{}{} + } + p.subscriptions.Store(relayerAddr, sub) + return nil + } + for _, c := range params.Chains { + sub[c] = struct{}{} + } + return nil +} + +func (p *pubSubManagerImpl) RemoveSubscription(relayerAddr string, params model.SubscriptionParams) error { + if params.Chains == nil { + return fmt.Errorf("chains is nil") + } + + sub, ok := p.subscriptions.Load(relayerAddr) + if !ok { + return fmt.Errorf("relayer %s has no subscriptions", relayerAddr) + } + + for _, c := range params.Chains { + _, ok := sub[c] + if !ok { + return fmt.Errorf("relayer %s is not subscribed to chain %d", relayerAddr, c) + } + delete(sub, c) + } + + return nil +} + +func (p *pubSubManagerImpl) IsSubscribed(relayerAddr string, origin, dest int) bool { + sub, ok := p.subscriptions.Load(relayerAddr) + if !ok { + return false + } + _, ok = sub[origin] + if !ok { + return false + } + _, ok = sub[dest] + return ok +} diff --git a/services/rfq/api/rest/rfq.go b/services/rfq/api/rest/rfq.go new file mode 100644 index 0000000000..770732c887 --- /dev/null +++ b/services/rfq/api/rest/rfq.go @@ -0,0 +1,272 @@ +package rest + +import ( + "context" + "errors" + "fmt" + "math/big" + "sync" + "time" + + "github.com/google/uuid" + "github.com/synapsecns/sanguine/core/metrics" + "github.com/synapsecns/sanguine/services/rfq/api/db" + "github.com/synapsecns/sanguine/services/rfq/api/model" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +const collectionTimeout = 1 * time.Minute + +func (r *QuoterAPIServer) handleActiveRFQ(ctx context.Context, request *model.PutRFQRequest, requestID string) (quote *model.QuoteData) { + ctx, span := r.handler.Tracer().Start(ctx, "handleActiveRFQ", trace.WithAttributes( + attribute.String("user_address", request.UserAddress), + attribute.String("request_id", requestID), + )) + defer func() { + metrics.EndSpan(span) + }() + + // publish the quote request to all connected clients + relayerReq := model.NewWsRFQRequest(request.Data, requestID) + r.wsClients.Range(func(relayerAddr string, client WsClient) bool { + sendCtx, sendSpan := r.handler.Tracer().Start(ctx, "sendQuoteRequest", trace.WithAttributes( + attribute.String("relayer_address", relayerAddr), + attribute.String("request_id", requestID), + )) + defer metrics.EndSpan(sendSpan) + + subscribed := r.pubSubManager.IsSubscribed(relayerAddr, request.Data.OriginChainID, request.Data.DestChainID) + span.SetAttributes(attribute.Bool("subscribed", subscribed)) + if subscribed { + err := client.SendQuoteRequest(sendCtx, relayerReq) + if err != nil { + logger.Errorf("Error sending quote request to %s: %v", relayerAddr, err) + } + } + return true + }) + err := r.db.UpdateActiveQuoteRequestStatus(ctx, requestID, nil, db.Pending) + if err != nil { + logger.Errorf("Error updating active quote request status: %v", err) + } + + // collect the responses and determine the best quote + responses := r.collectRelayerResponses(ctx, request, requestID) + for r, resp := range responses { + relayerAddr := r + quote = getBestQuote(quote, getRelayerQuoteData(request, resp)) + quote.RelayerAddress = &relayerAddr + } + err = r.recordActiveQuote(ctx, quote, requestID) + if err != nil { + logger.Errorf("Error recording active quote: %v", err) + } + + return quote +} + +func (r *QuoterAPIServer) collectRelayerResponses(ctx context.Context, request *model.PutRFQRequest, requestID string) (responses map[string]*model.WsRFQResponse) { + ctx, span := r.handler.Tracer().Start(ctx, "collectRelayerResponses", trace.WithAttributes( + attribute.String("user_address", request.UserAddress), + attribute.String("request_id", requestID), + )) + defer metrics.EndSpan(span) + + expireCtx, expireCancel := context.WithTimeout(ctx, time.Duration(request.Data.ExpirationWindow)*time.Millisecond) + defer expireCancel() + + // don't cancel the collection context so that late responses can be collected in background + // nolint:govet + collectionCtx, _ := context.WithTimeout(ctx, time.Duration(request.Data.ExpirationWindow)*time.Millisecond+collectionTimeout) + + wg := sync.WaitGroup{} + respMux := sync.Mutex{} + responses = map[string]*model.WsRFQResponse{} + r.wsClients.Range(func(relayerAddr string, client WsClient) bool { + wg.Add(1) + go func(client WsClient) { + var respStatus db.ActiveQuoteResponseStatus + var err error + _, clientSpan := r.handler.Tracer().Start(collectionCtx, "collectRelayerResponses", trace.WithAttributes( + attribute.String("relayer_address", relayerAddr), + attribute.String("request_id", requestID), + )) + defer func() { + clientSpan.SetAttributes(attribute.String("status", respStatus.String())) + metrics.EndSpanWithErr(clientSpan, err) + }() + + defer wg.Done() + resp, err := client.ReceiveQuoteResponse(collectionCtx, requestID) + if err != nil { + logger.Errorf("Error receiving quote response: %v", err) + return + } + clientSpan.AddEvent("received quote response", trace.WithAttributes( + attribute.String("relayer_address", relayerAddr), + attribute.String("request_id", requestID), + attribute.String("dest_amount", resp.DestAmount), + )) + + // validate the response + respStatus = getQuoteResponseStatus(expireCtx, resp) + if respStatus == db.Considered { + respMux.Lock() + responses[relayerAddr] = resp + respMux.Unlock() + } + + // record the response + err = r.db.InsertActiveQuoteResponse(collectionCtx, resp, relayerAddr, respStatus) + if err != nil { + logger.Errorf("Error inserting active quote response: %v", err) + } + }(client) + return true + }) + + // wait for all responses to be received, or expiration + select { + case <-expireCtx.Done(): + // request expired before all responses were received + case <-func() chan struct{} { + ch := make(chan struct{}) + go func() { + wg.Wait() + close(ch) + }() + return ch + }(): + // all responses received + } + + return responses +} + +func getRelayerQuoteData(request *model.PutRFQRequest, resp *model.WsRFQResponse) *model.QuoteData { + return &model.QuoteData{ + OriginChainID: request.Data.OriginChainID, + DestChainID: request.Data.DestChainID, + OriginTokenAddr: request.Data.OriginTokenAddr, + DestTokenAddr: request.Data.DestTokenAddr, + OriginAmount: request.Data.OriginAmount, + DestAmount: &resp.DestAmount, + QuoteID: &resp.QuoteID, + } +} + +func getBestQuote(a, b *model.QuoteData) *model.QuoteData { + if a == nil && b == nil { + return nil + } + if a == nil { + return b + } + if b == nil { + return a + } + aAmount, _ := new(big.Int).SetString(*a.DestAmount, 10) + bAmount, _ := new(big.Int).SetString(*b.DestAmount, 10) + if aAmount.Cmp(bAmount) > 0 { + return a + } + return b +} + +func getQuoteResponseStatus(ctx context.Context, resp *model.WsRFQResponse) db.ActiveQuoteResponseStatus { + respStatus := db.Considered + err := validateRelayerQuoteResponse(resp) + if err != nil { + respStatus = db.Malformed + logger.Errorf("Error validating quote response: %v", err) + } else if ctx.Err() != nil { + respStatus = db.PastExpiration + } + return respStatus +} + +func validateRelayerQuoteResponse(resp *model.WsRFQResponse) error { + _, ok := new(big.Int).SetString(resp.DestAmount, 10) + if !ok { + return fmt.Errorf("dest amount is invalid") + } + // TODO: compute quote ID from request + resp.QuoteID = uuid.New().String() + return nil +} + +func (r *QuoterAPIServer) recordActiveQuote(ctx context.Context, quote *model.QuoteData, requestID string) (err error) { + if quote == nil { + err = r.db.UpdateActiveQuoteRequestStatus(ctx, requestID, nil, db.Expired) + if err != nil { + logger.Errorf("Error updating active quote request status: %v", err) + } + } else { + err = r.db.UpdateActiveQuoteRequestStatus(ctx, requestID, quote.QuoteID, db.Closed) + if err != nil { + logger.Errorf("Error updating active quote request status: %v", err) + } + err = r.db.UpdateActiveQuoteResponseStatus(ctx, *quote.QuoteID, db.Returned) + if err != nil { + return fmt.Errorf("error updating active quote response status: %w", err) + } + } + return nil +} + +func (r *QuoterAPIServer) handlePassiveRFQ(ctx context.Context, request *model.PutRFQRequest) (*model.QuoteData, error) { + ctx, span := r.handler.Tracer().Start(ctx, "handlePassiveRFQ", trace.WithAttributes( + attribute.String("user_address", request.UserAddress), + )) + defer metrics.EndSpan(span) + + quotes, err := r.db.GetQuotesByOriginAndDestination(ctx, uint64(request.Data.OriginChainID), request.Data.OriginTokenAddr, uint64(request.Data.DestChainID), request.Data.DestTokenAddr) + if err != nil { + return nil, fmt.Errorf("failed to get quotes: %w", err) + } + + originAmount, ok := new(big.Int).SetString(request.Data.OriginAmount, 10) + if !ok { + return nil, errors.New("invalid origin amount") + } + + var bestQuote *model.QuoteData + for _, quote := range quotes { + quoteOriginAmount, ok := new(big.Int).SetString(quote.MaxOriginAmount.String(), 10) + if !ok { + continue + } + if quoteOriginAmount.Cmp(originAmount) < 0 { + continue + } + quotePrice := new(big.Float).Quo( + new(big.Float).SetInt(quote.DestAmount.BigInt()), + new(big.Float).SetInt(quote.MaxOriginAmount.BigInt()), + ) + + rawDestAmount := new(big.Float).Mul( + new(big.Float).SetInt(originAmount), + quotePrice, + ) + + rawDestAmountInt, _ := rawDestAmount.Int(nil) + if rawDestAmountInt.Cmp(quote.FixedFee.BigInt()) < 0 { + continue + } + destAmount := new(big.Int).Sub(rawDestAmountInt, quote.FixedFee.BigInt()).String() + //nolint:gosec + quoteData := &model.QuoteData{ + OriginChainID: int(quote.OriginChainID), + DestChainID: int(quote.DestChainID), + OriginTokenAddr: quote.OriginTokenAddr, + DestTokenAddr: quote.DestTokenAddr, + OriginAmount: quote.MaxOriginAmount.String(), + DestAmount: &destAmount, + RelayerAddress: "e.RelayerAddr, + } + bestQuote = getBestQuote(bestQuote, quoteData) + } + + return bestQuote, nil +} diff --git a/services/rfq/api/rest/rfq_test.go b/services/rfq/api/rest/rfq_test.go new file mode 100644 index 0000000000..624d48c588 --- /dev/null +++ b/services/rfq/api/rest/rfq_test.go @@ -0,0 +1,389 @@ +package rest_test + +import ( + "context" + "encoding/json" + "fmt" + "math/big" + + "github.com/shopspring/decimal" + "github.com/synapsecns/sanguine/core/metrics" + "github.com/synapsecns/sanguine/ethergo/signer/signer/localsigner" + "github.com/synapsecns/sanguine/ethergo/signer/wallet" + "github.com/synapsecns/sanguine/services/rfq/api/client" + "github.com/synapsecns/sanguine/services/rfq/api/db" + "github.com/synapsecns/sanguine/services/rfq/api/model" +) + +func runMockRelayer(c *ServerSuite, respCtx context.Context, relayerWallet wallet.Wallet, quoteResp *model.WsRFQResponse, url string) { + // Create a relayer client + relayerSigner := localsigner.NewSigner(relayerWallet.PrivateKey()) + relayerClient, err := client.NewAuthenticatedClient(metrics.Get(), url, relayerSigner) + c.Require().NoError(err) + + // Create channels for active quote requests and responses + reqChan := make(chan *model.ActiveRFQMessage) + req := &model.SubscribeActiveRFQRequest{ + ChainIDs: []int{c.originChainID, c.destChainID}, + } + respChan, err := relayerClient.SubscribeActiveQuotes(c.GetTestContext(), req, reqChan) + c.Require().NoError(err) + + go func() { + for { + select { + case <-respCtx.Done(): + return + case msg := <-respChan: + if msg == nil { + continue + } + if msg.Op == "request_quote" { + var quoteReq model.WsRFQRequest + err := json.Unmarshal(msg.Content, "eReq) + if err != nil { + c.Error(fmt.Errorf("error unmarshaling quote request: %w", err)) + continue + } + quoteResp.RequestID = quoteReq.RequestID + rawRespData, err := json.Marshal(quoteResp) + if err != nil { + c.Error(fmt.Errorf("error marshaling quote response: %w", err)) + continue + } + reqChan <- &model.ActiveRFQMessage{ + Op: "send_quote", + Content: json.RawMessage(rawRespData), + } + } + } + } + }() +} + +func verifyActiveQuoteRequest(c *ServerSuite, userReq *model.PutRFQRequest, activeQuoteRequest *db.ActiveQuoteRequest, status db.ActiveQuoteRequestStatus) { + c.Assert().Equal(uint64(userReq.Data.OriginChainID), activeQuoteRequest.OriginChainID) + c.Assert().Equal(userReq.Data.OriginTokenAddr, activeQuoteRequest.OriginTokenAddr) + c.Assert().Equal(uint64(userReq.Data.DestChainID), activeQuoteRequest.DestChainID) + c.Assert().Equal(userReq.Data.DestTokenAddr, activeQuoteRequest.DestTokenAddr) + c.Assert().Equal(userReq.Data.OriginAmount, activeQuoteRequest.OriginAmount.String()) + c.Assert().Equal(status, activeQuoteRequest.Status) +} + +const ( + originTokenAddr = "0x1111111111111111111111111111111111111111" + destTokenAddr = "0x2222222222222222222222222222222222222222" +) + +func (c *ServerSuite) TestActiveRFQSingleRelayer() { + // Start the API server + c.startQuoterAPIServer() + + url := fmt.Sprintf("http://localhost:%d", c.port) + + // Create a user client + userWallet, err := wallet.FromRandom() + c.Require().NoError(err) + userSigner := localsigner.NewSigner(userWallet.PrivateKey()) + userClient, err := client.NewAuthenticatedClient(metrics.Get(), url, userSigner) + c.Require().NoError(err) + + // Prepare a user quote request + userRequestAmount := big.NewInt(1_000_000) + userQuoteReq := &model.PutRFQRequest{ + Data: model.QuoteData{ + OriginChainID: c.originChainID, + OriginTokenAddr: originTokenAddr, + DestChainID: c.destChainID, + DestTokenAddr: destTokenAddr, + OriginAmount: userRequestAmount.String(), + ExpirationWindow: 10_000, + }, + QuoteTypes: []string{"active"}, + } + + // Prepare the relayer quote response + destAmount := new(big.Int).Sub(userRequestAmount, big.NewInt(1000)).String() + quoteResp := &model.WsRFQResponse{ + DestAmount: destAmount, + } + respCtx, cancel := context.WithCancel(c.GetTestContext()) + defer cancel() + runMockRelayer(c, respCtx, c.relayerWallets[0], quoteResp, url) + + // Submit the user quote request + userQuoteResp, err := userClient.PutRFQRequest(c.GetTestContext(), userQuoteReq) + c.Require().NoError(err) + + // Assert the response + c.Assert().True(userQuoteResp.Success) + c.Assert().Equal("active", userQuoteResp.QuoteType) + c.Assert().Equal(destAmount, userQuoteResp.DestAmount) + + // Verify ActiveQuoteRequest insertion + activeQuoteRequests, err := c.database.GetActiveQuoteRequests(c.GetTestContext()) + c.Require().NoError(err) + verifyActiveQuoteRequest(c, userQuoteReq, activeQuoteRequests[0], db.Closed) +} + +func (c *ServerSuite) TestActiveRFQExpiredRequest() { + // Start the API server + c.startQuoterAPIServer() + + url := fmt.Sprintf("http://localhost:%d", c.port) + + // Create a user client + userWallet, err := wallet.FromRandom() + c.Require().NoError(err) + userSigner := localsigner.NewSigner(userWallet.PrivateKey()) + userClient, err := client.NewAuthenticatedClient(metrics.Get(), url, userSigner) + c.Require().NoError(err) + + // Prepare a user quote request + userRequestAmount := big.NewInt(1_000_000) + userQuoteReq := &model.PutRFQRequest{ + Data: model.QuoteData{ + OriginChainID: c.originChainID, + OriginTokenAddr: originTokenAddr, + DestChainID: c.destChainID, + DestTokenAddr: destTokenAddr, + OriginAmount: userRequestAmount.String(), + ExpirationWindow: 0, + }, + QuoteTypes: []string{"active"}, + } + + // Prepare the relayer quote response + destAmount := new(big.Int).Sub(userRequestAmount, big.NewInt(1000)).String() + quoteResp := &model.WsRFQResponse{ + DestAmount: destAmount, + } + respCtx, cancel := context.WithCancel(c.GetTestContext()) + defer cancel() + runMockRelayer(c, respCtx, c.relayerWallets[0], quoteResp, url) + + // Submit the user quote request + userQuoteResp, err := userClient.PutRFQRequest(c.GetTestContext(), userQuoteReq) + c.Require().NoError(err) + + // Assert the response + c.Assert().False(userQuoteResp.Success) + c.Assert().Equal("no quotes found", userQuoteResp.Reason) + + // Verify ActiveQuoteRequest insertion + activeQuoteRequests, err := c.database.GetActiveQuoteRequests(c.GetTestContext()) + c.Require().NoError(err) + verifyActiveQuoteRequest(c, userQuoteReq, activeQuoteRequests[0], db.Expired) +} + +func (c *ServerSuite) TestActiveRFQMultipleRelayers() { + // Start the API server + c.startQuoterAPIServer() + + url := fmt.Sprintf("http://localhost:%d", c.port) + + // Create a user client + userWallet, err := wallet.FromRandom() + c.Require().NoError(err) + userSigner := localsigner.NewSigner(userWallet.PrivateKey()) + userClient, err := client.NewAuthenticatedClient(metrics.Get(), url, userSigner) + c.Require().NoError(err) + + // Prepare a user quote request + userRequestAmount := big.NewInt(1_000_000) + userQuoteReq := &model.PutRFQRequest{ + Data: model.QuoteData{ + OriginChainID: c.originChainID, + OriginTokenAddr: originTokenAddr, + DestChainID: c.destChainID, + DestTokenAddr: destTokenAddr, + OriginAmount: userRequestAmount.String(), + ExpirationWindow: 10_000, + }, + QuoteTypes: []string{"active"}, + } + + // Prepare the relayer quote responses + destAmount := "999000" + quoteResp := model.WsRFQResponse{ + DestAmount: destAmount, + } + + // Create additional responses with worse prices + destAmount2 := "998000" + quoteResp2 := model.WsRFQResponse{ + DestAmount: destAmount2, + } + destAmount3 := "997000" + quoteResp3 := model.WsRFQResponse{ + DestAmount: destAmount3, + } + respCtx, cancel := context.WithCancel(c.GetTestContext()) + defer cancel() + runMockRelayer(c, respCtx, c.relayerWallets[0], "eResp, url) + runMockRelayer(c, respCtx, c.relayerWallets[1], "eResp2, url) + runMockRelayer(c, respCtx, c.relayerWallets[2], "eResp3, url) + + // Submit the user quote request + userQuoteResp, err := userClient.PutRFQRequest(c.GetTestContext(), userQuoteReq) + c.Require().NoError(err) + + // Assert the response + c.Assert().True(userQuoteResp.Success) + c.Assert().Equal("active", userQuoteResp.QuoteType) + c.Assert().Equal(destAmount, userQuoteResp.DestAmount) + + // Verify ActiveQuoteRequest insertion + activeQuoteRequests, err := c.database.GetActiveQuoteRequests(c.GetTestContext()) + c.Require().NoError(err) + verifyActiveQuoteRequest(c, userQuoteReq, activeQuoteRequests[0], db.Closed) +} + +func (c *ServerSuite) TestActiveRFQFallbackToPassive() { + // Start the API server + c.startQuoterAPIServer() + + url := fmt.Sprintf("http://localhost:%d", c.port) + + // Create a user client + userWallet, err := wallet.FromRandom() + c.Require().NoError(err) + userSigner := localsigner.NewSigner(userWallet.PrivateKey()) + userClient, err := client.NewAuthenticatedClient(metrics.Get(), url, userSigner) + c.Require().NoError(err) + + userRequestAmount := big.NewInt(1_000_000) + + // Upsert passive quotes into the database + passiveQuotes := []db.Quote{ + { + RelayerAddr: c.relayerWallets[0].Address().Hex(), + OriginChainID: uint64(c.originChainID), + OriginTokenAddr: originTokenAddr, + DestChainID: uint64(c.destChainID), + DestTokenAddr: destTokenAddr, + DestAmount: decimal.NewFromBigInt(new(big.Int).Sub(userRequestAmount, big.NewInt(1000)), 0), + MaxOriginAmount: decimal.NewFromBigInt(userRequestAmount, 0), + FixedFee: decimal.NewFromInt(1000), + }, + } + + for _, quote := range passiveQuotes { + err := c.database.UpsertQuote(c.GetTestContext(), "e) + c.Require().NoError(err) + } + + // Prepare user quote request with 0 expiration window + userQuoteReq := &model.PutRFQRequest{ + Data: model.QuoteData{ + OriginChainID: c.originChainID, + OriginTokenAddr: originTokenAddr, + DestChainID: c.destChainID, + DestTokenAddr: destTokenAddr, + OriginAmount: userRequestAmount.String(), + ExpirationWindow: 0, + }, + QuoteTypes: []string{"active", "passive"}, + } + + // Prepare mock relayer response (which should be ignored due to 0 expiration window) + destAmount := new(big.Int).Sub(userRequestAmount, big.NewInt(1000)).String() + quoteResp := &model.WsRFQResponse{ + DestAmount: destAmount, + } + + respCtx, cancel := context.WithCancel(c.GetTestContext()) + defer cancel() + + // Run mock relayer even though we expect it to be ignored + runMockRelayer(c, respCtx, c.relayerWallets[0], quoteResp, url) + + // Submit the user quote request + userQuoteResp, err := userClient.PutRFQRequest(c.GetTestContext(), userQuoteReq) + c.Require().NoError(err) + + // Assert the response + c.Assert().True(userQuoteResp.Success) + c.Assert().Equal("passive", userQuoteResp.QuoteType) + c.Assert().Equal("998000", userQuoteResp.DestAmount) // destAmount is quote destAmount minus fixed fee + c.Assert().Equal(c.relayerWallets[0].Address().Hex(), userQuoteResp.RelayerAddress) +} + +func (c *ServerSuite) TestActiveRFQPassiveBestQuote() { + // Start the API server + c.startQuoterAPIServer() + + url := fmt.Sprintf("http://localhost:%d", c.port) + + // Create a user client + userWallet, err := wallet.FromRandom() + c.Require().NoError(err) + userSigner := localsigner.NewSigner(userWallet.PrivateKey()) + userClient, err := client.NewAuthenticatedClient(metrics.Get(), url, userSigner) + c.Require().NoError(err) + + userRequestAmount := big.NewInt(1_000_000) + + // Upsert passive quotes into the database + passiveQuotes := []db.Quote{ + { + RelayerAddr: c.relayerWallets[0].Address().Hex(), + OriginChainID: uint64(c.originChainID), + OriginTokenAddr: originTokenAddr, + DestChainID: uint64(c.destChainID), + DestTokenAddr: destTokenAddr, + DestAmount: decimal.NewFromBigInt(new(big.Int).Sub(userRequestAmount, big.NewInt(100)), 0), + MaxOriginAmount: decimal.NewFromBigInt(userRequestAmount, 0), + FixedFee: decimal.NewFromInt(1000), + }, + } + + for _, quote := range passiveQuotes { + err := c.database.UpsertQuote(c.GetTestContext(), "e) + c.Require().NoError(err) + } + + // Prepare user quote request with 0 expiration window + userQuoteReq := &model.PutRFQRequest{ + Data: model.QuoteData{ + OriginChainID: c.originChainID, + OriginTokenAddr: originTokenAddr, + DestChainID: c.destChainID, + DestTokenAddr: destTokenAddr, + OriginAmount: userRequestAmount.String(), + ExpirationWindow: 0, + }, + QuoteTypes: []string{"active", "passive"}, + } + + // Prepare mock relayer response (which should be ignored due to 0 expiration window) + destAmount := new(big.Int).Sub(userRequestAmount, big.NewInt(1000)).String() + quoteResp := model.WsRFQResponse{ + DestAmount: destAmount, + } + + respCtx, cancel := context.WithCancel(c.GetTestContext()) + defer cancel() + + // Create additional responses with worse prices + quoteResp2 := quoteResp + destAmount2 := new(big.Int).Sub(userRequestAmount, big.NewInt(2000)) + quoteResp2.DestAmount = destAmount2.String() + quoteResp3 := quoteResp + destAmount3 := new(big.Int).Sub(userRequestAmount, big.NewInt(3000)) + quoteResp3.DestAmount = destAmount3.String() + + runMockRelayer(c, respCtx, c.relayerWallets[0], "eResp, url) + runMockRelayer(c, respCtx, c.relayerWallets[1], "eResp2, url) + runMockRelayer(c, respCtx, c.relayerWallets[2], "eResp3, url) + + // Submit the user quote request + userQuoteResp, err := userClient.PutRFQRequest(c.GetTestContext(), userQuoteReq) + c.Require().NoError(err) + + // Assert the response + c.Assert().True(userQuoteResp.Success) + c.Assert().Equal("passive", userQuoteResp.QuoteType) + c.Assert().Equal("998900", userQuoteResp.DestAmount) // destAmount is quote destAmount minus fixed fee + c.Assert().Equal(c.relayerWallets[0].Address().Hex(), userQuoteResp.RelayerAddress) +} diff --git a/services/rfq/api/rest/server.go b/services/rfq/api/rest/server.go index a3a3b32a6f..5470e0d4f0 100644 --- a/services/rfq/api/rest/server.go +++ b/services/rfq/api/rest/server.go @@ -3,23 +3,28 @@ package rest import ( "context" + "encoding/json" "fmt" "net/http" "sync" "time" + "github.com/google/uuid" "github.com/ipfs/go-log" + "github.com/puzpuzpuz/xsync" swaggerfiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" "github.com/synapsecns/sanguine/core/ginhelper" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/trace" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" "github.com/jellydator/ttlcache/v3" "github.com/synapsecns/sanguine/core/metrics" baseServer "github.com/synapsecns/sanguine/core/server" @@ -48,6 +53,7 @@ type QuoterAPIServer struct { cfg config.Config db db.APIDB engine *gin.Engine + upgrader websocket.Upgrader omnirpcClient omniClient.RPCClient handler metrics.Handler meter metric.Meter @@ -58,8 +64,11 @@ type QuoterAPIServer struct { relayAckCache *ttlcache.Cache[string, string] // ackMux is a mutex used to ensure that only one transaction id can be acked at a time. ackMux sync.Mutex - // latestQuoteAgeGauge is a gauge that records the age of the latest quote + // latestQuoteAgeGauge is a gauge that records the age of the latest quote. latestQuoteAgeGauge metric.Float64ObservableGauge + // wsClients maintains a mapping of connection ID to a channel for sending quote requests. + wsClients *xsync.MapOf[string, WsClient] + pubSubManager PubSubManager } // NewAPI holds the configuration, database connection, gin engine, RPC client, metrics handler, and fast bridge contracts. @@ -131,6 +140,8 @@ func NewAPI( roleCache: roles, relayAckCache: relayAckCache, ackMux: sync.Mutex{}, + wsClients: xsync.NewMapOf[WsClient](), + pubSubManager: NewPubSubManager(), } // Prometheus metrics setup @@ -157,14 +168,21 @@ const ( AckRoute = "/ack" // ContractsRoute is the API endpoint for returning a list fo contracts. ContractsRoute = "/contracts" - cacheInterval = time.Minute + // RFQStreamRoute is the API endpoint for handling active quote requests via websocket. + RFQStreamRoute = "/rfq_stream" + // RFQRoute is the API endpoint for handling RFQ requests. + RFQRoute = "/rfq" + // ChainsHeader is the header for specifying chains during a websocket handshake. + ChainsHeader = "Chains" + // AuthorizationHeader is the header for specifying the authorization. + AuthorizationHeader = "Authorization" + cacheInterval = time.Minute ) var logger = log.Logger("rfq-api") // Run runs the quoter api server. func (r *QuoterAPIServer) Run(ctx context.Context) error { - // TODO: Use Gin Helper engine := ginhelper.New(logger) h := NewHandler(r.db, r.cfg) @@ -175,7 +193,7 @@ func (r *QuoterAPIServer) Run(ctx context.Context) error { engine.Use(APIVersionMiddleware(versionNumber)) engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler)) - // Apply AuthMiddleware only to the PUT routes + // Authenticated routes quotesPut := engine.Group(QuoteRoute) quotesPut.Use(r.AuthMiddleware()) quotesPut.PUT("", h.ModifyQuote) @@ -185,17 +203,35 @@ func (r *QuoterAPIServer) Run(ctx context.Context) error { ackPut := engine.Group(AckRoute) ackPut.Use(r.AuthMiddleware()) ackPut.PUT("", r.PutRelayAck) - - // GET routes without the AuthMiddleware - // engine.PUT("/quotes", h.ModifyQuote) + openQuoteRequestsGet := engine.Group(RFQRoute) + openQuoteRequestsGet.Use(r.AuthMiddleware()) + openQuoteRequestsGet.GET("", h.GetOpenQuoteRequests) + + // WebSocket route + wsRoute := engine.Group(RFQStreamRoute) + wsRoute.Use(r.AuthMiddleware()) + wsRoute.GET("", func(c *gin.Context) { + r.GetActiveRFQWebsocket(ctx, c) + }) + + // Unauthenticated routes engine.GET(QuoteRoute, h.GetQuotes) - engine.GET(ContractsRoute, h.GetContracts) + engine.PUT(RFQRoute, r.PutRFQRequest) + + // WebSocket upgrader + r.upgrader = websocket.Upgrader{ + CheckOrigin: func(_ *http.Request) bool { + return true // TODO: Implement a more secure check + }, + } r.engine = engine + // Start the main HTTP server connection := baseServer.Server{} fmt.Printf("starting api at http://localhost:%s\n", r.cfg.Port) + err := connection.ListenAndServe(ctx, fmt.Sprintf(":%s", r.cfg.Port), r.engine) if err != nil { return fmt.Errorf("could not start rest api server: %w", err) @@ -205,6 +241,8 @@ func (r *QuoterAPIServer) Run(ctx context.Context) error { } // AuthMiddleware is the Gin authentication middleware that authenticates requests using EIP191. +// +//nolint:gosec func (r *QuoterAPIServer) AuthMiddleware() gin.HandlerFunc { return func(c *gin.Context) { var loggedRequest interface{} @@ -214,7 +252,7 @@ func (r *QuoterAPIServer) AuthMiddleware() gin.HandlerFunc { // Parse the dest chain id from the request switch c.Request.URL.Path { case QuoteRoute: - var req model.PutQuoteRequest + var req model.PutRelayerQuoteRequest err = c.BindJSON(&req) if err == nil { destChainIDs = append(destChainIDs, uint32(req.DestChainID)) @@ -236,6 +274,17 @@ func (r *QuoterAPIServer) AuthMiddleware() gin.HandlerFunc { destChainIDs = append(destChainIDs, uint32(req.DestChainID)) loggedRequest = &req } + case RFQRoute, RFQStreamRoute: + chainsHeader := c.GetHeader(ChainsHeader) + if chainsHeader != "" { + var chainIDs []int + err = json.Unmarshal([]byte(chainsHeader), &chainIDs) + if err == nil { + for _, chainID := range chainIDs { + destChainIDs = append(destChainIDs, uint32(chainID)) + } + } + } default: err = fmt.Errorf("unexpected request path: %s", c.Request.URL.Path) } @@ -325,7 +374,7 @@ func (r *QuoterAPIServer) checkRole(c *gin.Context, destChainID uint32) (address // @Summary Relay ack // @Schemes // @Description cache an ack request to synchronize relayer actions. -// @Param request body model.PutQuoteRequest true "query params" +// @Param request body model.PutRelayerQuoteRequest true "query params" // @Tags ack // @Accept json // @Produce json @@ -374,6 +423,159 @@ func (r *QuoterAPIServer) PutRelayAck(c *gin.Context) { c.JSON(http.StatusOK, resp) } +// GetActiveRFQWebsocket handles the WebSocket connection for active quote requests. +// GET /rfq_stream. +// @Summary Handle WebSocket connection for active quote requests +// @Schemes +// @Description Establish a WebSocket connection to receive active quote requests. +// @Tags quotes +// @Produce json +// @Success 101 {string} string "Switching Protocols" +// @Header 101 {string} X-Api-Version "API Version Number - See docs for more info" +// @Router /rfq_stream [get]. +func (r *QuoterAPIServer) GetActiveRFQWebsocket(ctx context.Context, c *gin.Context) { + ctx, span := r.handler.Tracer().Start(ctx, "GetActiveRFQWebsocket") + defer func() { + metrics.EndSpan(span) + }() + + ws, err := r.upgrader.Upgrade(c.Writer, c.Request, nil) + if err != nil { + logger.Error("Failed to set websocket upgrade", "error", err) + return + } + + // use the relayer address as the ID for the connection + rawRelayerAddr, exists := c.Get("relayerAddr") + if !exists { + c.JSON(http.StatusBadRequest, gin.H{"error": "No relayer address recovered from signature"}) + return + } + relayerAddr, ok := rawRelayerAddr.(string) + if !ok { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid relayer address type"}) + return + } + + span.SetAttributes( + attribute.String("relayer_address", relayerAddr), + ) + + // only one connection per relayer allowed + _, ok = r.wsClients.Load(relayerAddr) + if ok { + c.JSON(http.StatusBadRequest, gin.H{"error": "relayer already connected"}) + return + } + + defer func() { + // cleanup ws registry + r.wsClients.Delete(relayerAddr) + }() + + client := newWsClient(relayerAddr, ws, r.pubSubManager, r.handler) + r.wsClients.Store(relayerAddr, client) + span.AddEvent("registered ws client") + err = client.Run(ctx) + if err != nil { + logger.Error("Error running websocket client", "error", err) + } +} + +const ( + quoteTypeActive = "active" + quoteTypePassive = "passive" +) + +// PutRFQRequest handles a user request for a quote. +// PUT /rfq. +// @Summary Handle user quote request +// @Schemes +// @Description Handle user quote request and return the best quote available. +// @Param request body model.PutRFQRequest true "User quote request" +// @Tags quotes +// @Accept json +// @Produce json +// @Success 200 {object} model.PutRFQResponse +// @Header 200 {string} X-Api-Version "API Version Number - See docs for more info" +// @Router /rfq [put]. +// +//nolint:cyclop +func (r *QuoterAPIServer) PutRFQRequest(c *gin.Context) { + var req model.PutRFQRequest + err := c.BindJSON(&req) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + requestID := uuid.New().String() + ctx, span := r.handler.Tracer().Start(c.Request.Context(), "PutRFQRequest", trace.WithAttributes( + attribute.String("request_id", requestID), + )) + defer func() { + metrics.EndSpan(span) + }() + + err = r.db.InsertActiveQuoteRequest(ctx, &req, requestID) + if err != nil { + logger.Warnf("Error inserting active quote request: %w", err) + } + + var isActiveRFQ bool + for _, quoteType := range req.QuoteTypes { + if quoteType == quoteTypeActive { + isActiveRFQ = true + break + } + } + span.SetAttributes(attribute.Bool("is_active_rfq", isActiveRFQ)) + + // if specified, fetch the active quote. always consider passive quotes + var activeQuote *model.QuoteData + if isActiveRFQ { + activeQuote = r.handleActiveRFQ(ctx, &req, requestID) + if activeQuote != nil && activeQuote.DestAmount != nil { + span.SetAttributes(attribute.String("active_quote_dest_amount", *activeQuote.DestAmount)) + } + } + passiveQuote, err := r.handlePassiveRFQ(ctx, &req) + if err != nil { + logger.Error("Error handling passive RFQ", "error", err) + } + if passiveQuote != nil && passiveQuote.DestAmount != nil { + span.SetAttributes(attribute.String("passive_quote_dest_amount", *passiveQuote.DestAmount)) + } + quote := getBestQuote(activeQuote, passiveQuote) + + // construct the response + var resp model.PutRFQResponse + if quote == nil { + span.AddEvent("no quotes found") + resp = model.PutRFQResponse{ + Success: false, + Reason: "no quotes found", + } + } else { + quoteType := quoteTypeActive + if activeQuote == nil { + quoteType = quoteTypePassive + } + span.SetAttributes( + attribute.String("quote_type", quoteType), + attribute.String("quote_dest_amount", *quote.DestAmount), + ) + resp = model.PutRFQResponse{ + Success: true, + QuoteType: quoteType, + QuoteID: quote.QuoteID, + DestAmount: *quote.DestAmount, + RelayerAddress: *quote.RelayerAddress, + } + } + c.JSON(http.StatusOK, resp) +} + func (r *QuoterAPIServer) recordLatestQuoteAge(ctx context.Context, observer metric.Observer) (err error) { if r.handler == nil || r.latestQuoteAgeGauge == nil { return nil diff --git a/services/rfq/api/rest/server_test.go b/services/rfq/api/rest/server_test.go index 50245f6ac4..c82db8d6fc 100644 --- a/services/rfq/api/rest/server_test.go +++ b/services/rfq/api/rest/server_test.go @@ -125,9 +125,6 @@ func (c *ServerSuite) TestEIP191_UnsuccessfulSignature() { err = resp.Body.Close() c.Require().NoError(err) }() - // Log the response body for debugging. - body, _ := io.ReadAll(resp.Body) - fmt.Println(string(body)) // Assert that the response status code is HTTP 400 Bad Request. c.Equal(http.StatusBadRequest, resp.StatusCode) @@ -152,11 +149,6 @@ func (c *ServerSuite) TestEIP191_SuccessfulPutSubmission() { // Check for X-Api-Version on the response c.Equal(resp.Header.Get("X-Api-Version"), rest.APIversions.Versions[0].Version) - // Log the response body for debugging. - body, err := io.ReadAll(resp.Body) - c.Require().NoError(err) - fmt.Println(string(body)) - // Assert that the response status code is HTTP 200 OK. c.Assert().Equal(http.StatusOK, resp.StatusCode) } @@ -209,6 +201,87 @@ func (c *ServerSuite) TestPutAndGetQuote() { c.Assert().True(found, "Newly added quote not found") } +func (c *ServerSuite) TestGetOpenQuoteRequests() { + // Start the API server + c.startQuoterAPIServer() + + // Insert some test quote requests + testRequests := []*model.PutRFQRequest{ + { + Data: model.QuoteData{ + OriginChainID: 1, + DestChainID: 42161, + OriginTokenAddr: "0xOriginTokenAddr", + DestTokenAddr: "0xDestTokenAddr", + OriginAmount: "100.0", + ExpirationWindow: 100, + }, + }, + { + Data: model.QuoteData{ + OriginChainID: 1, + DestChainID: 42161, + OriginTokenAddr: "0xOriginTokenAddr", + DestTokenAddr: "0xDestTokenAddr", + OriginAmount: "100.0", + ExpirationWindow: 100, + }, + }, + { + Data: model.QuoteData{ + OriginChainID: 1, + DestChainID: 42161, + OriginTokenAddr: "0xOriginTokenAddr", + DestTokenAddr: "0xDestTokenAddr", + OriginAmount: "100.0", + ExpirationWindow: 100, + }, + }, + } + + statuses := []db.ActiveQuoteRequestStatus{db.Received, db.Pending, db.Expired} + for i, req := range testRequests { + err := c.database.InsertActiveQuoteRequest(c.GetTestContext(), req, strconv.Itoa(i)) + c.Require().NoError(err) + err = c.database.UpdateActiveQuoteRequestStatus(c.GetTestContext(), strconv.Itoa(i), nil, statuses[i]) + c.Require().NoError(err) + } + + // Prepare the authorization header + header, err := c.prepareAuthHeader(c.testWallet) + c.Require().NoError(err) + + // Send GET request to fetch open quote requests + client := &http.Client{} + req, err := http.NewRequestWithContext(c.GetTestContext(), http.MethodGet, fmt.Sprintf("http://localhost:%d%s", c.port, rest.RFQRoute), nil) + c.Require().NoError(err) + req.Header.Add("Authorization", header) + chainIDsJSON, err := json.Marshal([]uint64{1, 42161}) + c.Require().NoError(err) + req.Header.Add("Chains", string(chainIDsJSON)) + + resp, err := client.Do(req) + c.Require().NoError(err) + defer func() { + err = resp.Body.Close() + c.Require().NoError(err) + }() + + // Check the response status code + c.Assert().Equal(http.StatusOK, resp.StatusCode) + + // Check for X-Api-Version on the response + c.Equal(resp.Header.Get("X-Api-Version"), rest.APIversions.Versions[0].Version) + + // Parse the response body + var openRequests []*model.GetOpenQuoteRequestsResponse + err = json.NewDecoder(resp.Body).Decode(&openRequests) + c.Require().NoError(err) + + // Verify the number of open requests (should be 2: Received and Pending) + c.Assert().Len(openRequests, 2) +} + func (c *ServerSuite) TestPutAndGetQuoteByRelayer() { c.startQuoterAPIServer() @@ -285,9 +358,6 @@ func (c *ServerSuite) TestMultiplePutRequestsWithIncorrectAuth() { // Check for X-Api-Version on the response c.Equal(resp.Header.Get("X-Api-Version"), rest.APIversions.Versions[0].Version) - // Log the response body for debugging - fmt.Printf("Request %d response: Status: %d, Body: %s\n", i+1, resp.StatusCode, string(body)) - switch resp.StatusCode { case http.StatusBadRequest, http.StatusUnauthorized, http.StatusForbidden: // These are acceptable error status codes for failed authentication @@ -401,9 +471,7 @@ func (c *ServerSuite) prepareAuthHeader(wallet wallet.Wallet) (string, error) { func (c *ServerSuite) sendPutQuoteRequest(header string) (*http.Response, error) { // Prepare the PUT request with JSON data. client := &http.Client{} - putData := model.PutQuoteRequest{ - OriginChainID: 1, - OriginTokenAddr: "0xOriginTokenAddr", + putData := model.PutRelayerQuoteRequest{ DestChainID: 42161, DestTokenAddr: "0xDestTokenAddr", DestAmount: "100.0", diff --git a/services/rfq/api/rest/suite_test.go b/services/rfq/api/rest/suite_test.go index 112e957ae1..755b4882ca 100644 --- a/services/rfq/api/rest/suite_test.go +++ b/services/rfq/api/rest/suite_test.go @@ -40,29 +40,37 @@ type ServerSuite struct { database db.APIDB cfg config.Config testWallet wallet.Wallet + relayerWallets []wallet.Wallet handler metrics.Handler QuoterAPIServer *rest.QuoterAPIServer port uint16 + originChainID int + destChainID int } // NewServerSuite creates a end-to-end test suite. func NewServerSuite(tb testing.TB) *ServerSuite { tb.Helper() return &ServerSuite{ - TestSuite: testsuite.NewTestSuite(tb), + TestSuite: testsuite.NewTestSuite(tb), + relayerWallets: []wallet.Wallet{}, } } +//nolint:gosec func (c *ServerSuite) SetupTest() { c.TestSuite.SetupTest() + c.setDB() testOmnirpc := omnirpcHelper.NewOmnirpcServer(c.GetTestContext(), c.T(), c.omniRPCTestBackends...) omniRPCClient := omniClient.NewOmnirpcClient(testOmnirpc, c.handler, omniClient.WithCaptureReqRes()) c.omniRPCClient = omniRPCClient - arbFastBridgeAddress, ok := c.fastBridgeAddressMap.Load(42161) + c.originChainID = 1 + c.destChainID = 42161 + arbFastBridgeAddress, ok := c.fastBridgeAddressMap.Load(uint64(c.destChainID)) c.True(ok) - ethFastBridgeAddress, ok := c.fastBridgeAddressMap.Load(1) + ethFastBridgeAddress, ok := c.fastBridgeAddressMap.Load(uint64(c.originChainID)) c.True(ok) port, err := freeport.GetFreePort() c.port = uint16(port) @@ -132,8 +140,16 @@ func (c *ServerSuite) SetupSuite() { testWallet, err := wallet.FromRandom() c.Require().NoError(err) c.testWallet = testWallet + c.relayerWallets = []wallet.Wallet{c.testWallet} + for range [2]int{} { + relayerWallet, err := wallet.FromRandom() + c.Require().NoError(err) + c.relayerWallets = append(c.relayerWallets, relayerWallet) + } for _, backend := range c.testBackends { - backend.FundAccount(c.GetSuiteContext(), c.testWallet.Address(), *big.NewInt(params.Ether)) + for _, relayerWallet := range c.relayerWallets { + backend.FundAccount(c.GetSuiteContext(), relayerWallet.Address(), *big.NewInt(params.Ether)) + } } c.fastBridgeAddressMap = xsync.NewIntegerMapOf[uint64, common.Address]() @@ -163,9 +179,12 @@ func (c *ServerSuite) SetupSuite() { relayerRole, err := fastBridgeInstance.RELAYERROLE(&bind.CallOpts{Context: c.GetTestContext()}) c.NoError(err) - tx, err = fastBridgeInstance.GrantRole(auth, relayerRole, c.testWallet.Address()) - c.Require().NoError(err) - backend.WaitForConfirmation(c.GetSuiteContext(), tx) + // Grant relayer role to all relayer wallets + for _, relayerWallet := range c.relayerWallets { + tx, err = fastBridgeInstance.GrantRole(auth, relayerRole, relayerWallet.Address()) + c.Require().NoError(err) + backend.WaitForConfirmation(c.GetSuiteContext(), tx) + } return nil }) @@ -175,7 +194,10 @@ func (c *ServerSuite) SetupSuite() { if err := g.Wait(); err != nil { c.T().Fatal(err) } + // setup config +} +func (c *ServerSuite) setDB() { dbType, err := dbcommon.DBTypeFromString("sqlite") c.Require().NoError(err) metricsHandler := metrics.NewNullHandler() @@ -183,7 +205,6 @@ func (c *ServerSuite) SetupSuite() { // TODO use temp file / in memory sqlite3 to not create in directory files testDB, _ := sql.Connect(c.GetSuiteContext(), dbType, filet.TmpDir(c.T(), ""), metricsHandler) c.database = testDB - // setup config } // TestConfigSuite runs the integration test suite. diff --git a/services/rfq/api/rest/ws.go b/services/rfq/api/rest/ws.go new file mode 100644 index 0000000000..99ba0cfa19 --- /dev/null +++ b/services/rfq/api/rest/ws.go @@ -0,0 +1,340 @@ +package rest + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/gorilla/websocket" + "github.com/puzpuzpuz/xsync" + "github.com/synapsecns/sanguine/core/metrics" + "github.com/synapsecns/sanguine/services/rfq/api/model" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + "golang.org/x/sync/errgroup" +) + +// WsClient is a client for the WebSocket API. +type WsClient interface { + Run(ctx context.Context) error + SendQuoteRequest(ctx context.Context, quoteRequest *model.WsRFQRequest) error + ReceiveQuoteResponse(ctx context.Context, requestID string) (*model.WsRFQResponse, error) +} + +type wsClient struct { + handler metrics.Handler + relayerAddr string + conn *websocket.Conn + pubsub PubSubManager + requestChan chan *model.WsRFQRequest + responseChans *xsync.MapOf[string, chan *model.WsRFQResponse] + doneChan chan struct{} + pingTicker *time.Ticker + lastPing time.Time +} + +func newWsClient(relayerAddr string, conn *websocket.Conn, pubsub PubSubManager, handler metrics.Handler) *wsClient { + return &wsClient{ + handler: handler, + relayerAddr: relayerAddr, + conn: conn, + pubsub: pubsub, + requestChan: make(chan *model.WsRFQRequest), + responseChans: xsync.NewMapOf[chan *model.WsRFQResponse](), + doneChan: make(chan struct{}), + pingTicker: time.NewTicker(pingPeriod), + } +} + +func (c *wsClient) SendQuoteRequest(ctx context.Context, quoteRequest *model.WsRFQRequest) error { + select { + case c.requestChan <- quoteRequest: + // successfully sent, register a response channel + c.responseChans.Store(quoteRequest.RequestID, make(chan *model.WsRFQResponse)) + case <-c.doneChan: + return fmt.Errorf("websocket client is closed") + case <-ctx.Done(): + return nil + } + return nil +} + +func (c *wsClient) ReceiveQuoteResponse(ctx context.Context, requestID string) (resp *model.WsRFQResponse, err error) { + responseChan, ok := c.responseChans.Load(requestID) + if !ok { + return nil, fmt.Errorf("no response channel for request %s", requestID) + } + defer c.responseChans.Delete(requestID) + + for { + select { + case resp = <-responseChan: + // successfully received + return resp, nil + case <-c.doneChan: + return nil, fmt.Errorf("websocket client is closed") + case <-ctx.Done(): + return nil, fmt.Errorf("expiration reached without response") + } + } +} + +const ( + // PongOp is the operation for a pong message. + PongOp = "pong" + // PingOp is the operation for a ping message. + PingOp = "ping" + // SubscribeOp is the operation for a subscribe message. + SubscribeOp = "subscribe" + // UnsubscribeOp is the operation for an unsubscribe message. + UnsubscribeOp = "unsubscribe" + // RequestQuoteOp is the operation for a request quote message. + RequestQuoteOp = "request_quote" + // SendQuoteOp is the operation for a send quote message. + SendQuoteOp = "send_quote" + // pingPeriod is the period for a ping message. + pingPeriod = 1 * time.Minute +) + +// Run runs the WebSocket client. +func (c *wsClient) Run(ctx context.Context) (err error) { + messageChan := make(chan []byte) + + g, gctx := errgroup.WithContext(ctx) + g.Go(func() error { + err := pollWsMessages(gctx, c.conn, messageChan) + if err != nil { + return fmt.Errorf("error polling websocket messages: %w", err) + } + return nil + }) + g.Go(func() error { + err := c.processWs(gctx, messageChan) + if err != nil { + return fmt.Errorf("error processing websocket messages: %w", err) + } + return nil + }) + + err = g.Wait() + if err != nil { + return fmt.Errorf("error running websocket client: %w", err) + } + + return nil +} + +func pollWsMessages(ctx context.Context, conn *websocket.Conn, messageChan chan []byte) (err error) { + defer close(messageChan) + for { + _, msg, err := conn.ReadMessage() + if err != nil { + return fmt.Errorf("error reading websocket message: %w", err) + } + select { + case <-ctx.Done(): + return nil + case messageChan <- msg: + } + } +} + +func (c *wsClient) processWs(ctx context.Context, messageChan chan []byte) (err error) { + defer c.pingTicker.Stop() + + for { + select { + case <-ctx.Done(): + err = c.conn.Close() + if err != nil { + return fmt.Errorf("error closing websocket connection: %w", err) + } + close(c.doneChan) + return fmt.Errorf("websocket client is closed") + case req := <-c.requestChan: + err = c.sendRelayerRequest(ctx, req) + if err != nil { + logger.Error("Error sending quote request: %s", err) + } + case msg := <-messageChan: + err = c.handleRelayerMessage(ctx, msg) + if err != nil { + logger.Error("Error handling relayer message: %s", err) + return fmt.Errorf("error handling relayer message: %w", err) + } + case <-c.pingTicker.C: + // ping timed out, close the connection + _, span := c.handler.Tracer().Start(ctx, "pingTimeout") + defer metrics.EndSpanWithErr(span, err) + } + } +} + +func (c *wsClient) sendRelayerRequest(ctx context.Context, req *model.WsRFQRequest) (err error) { + _, span := c.handler.Tracer().Start(ctx, "sendRelayerRequest", trace.WithAttributes( + attribute.String("relayer_address", c.relayerAddr), + attribute.String("request_id", req.RequestID), + )) + defer func() { + metrics.EndSpan(span) + }() + + rawData, err := json.Marshal(req) + if err != nil { + return fmt.Errorf("error marshaling quote request: %w", err) + } + msg := model.ActiveRFQMessage{ + Op: RequestQuoteOp, + Content: json.RawMessage(rawData), + } + err = c.conn.WriteJSON(msg) + if err != nil { + return fmt.Errorf("error sending quote request: %w", err) + } + + return nil +} + +// handleRelayerMessage handles messages from the relayer. +// An error returned will result in the websocket connection being closed. +func (c *wsClient) handleRelayerMessage(ctx context.Context, msg []byte) (err error) { + _, span := c.handler.Tracer().Start(ctx, "handleRelayerMessage", trace.WithAttributes( + attribute.String("relayer_address", c.relayerAddr), + attribute.String("message", string(msg)), + )) + defer func() { + metrics.EndSpanWithErr(span, err) + }() + + var rfqMsg model.ActiveRFQMessage + err = json.Unmarshal(msg, &rfqMsg) + if err != nil { + return fmt.Errorf("error unmarshaling websocket message: %w", err) + } + + switch rfqMsg.Op { + case PingOp: + c.lastPing = time.Now() + resp := c.handlePing(ctx) + err = c.conn.WriteJSON(resp) + if err != nil { + return fmt.Errorf("error sending ping response: %w", err) + } + case SubscribeOp: + resp := c.handleSubscribe(ctx, rfqMsg.Content) + err = c.conn.WriteJSON(resp) + if err != nil { + return fmt.Errorf("error sending subscribe response: %w", err) + } + case UnsubscribeOp: + resp := c.handleUnsubscribe(ctx, rfqMsg.Content) + err = c.conn.WriteJSON(resp) + if err != nil { + return fmt.Errorf("error sending unsubscribe response: %w", err) + } + case SendQuoteOp: + err = c.handleSendQuote(ctx, rfqMsg.Content) + logger.Errorf("error handling send quote: %v", err) + default: + logger.Errorf("received unexpected operation from relayer: %s", rfqMsg.Op) + return nil + } + + return nil +} + +func (c *wsClient) handlePing(ctx context.Context) (resp model.ActiveRFQMessage) { + _, span := c.handler.Tracer().Start(ctx, "handlePing", trace.WithAttributes( + attribute.String("relayer_address", c.relayerAddr), + )) + defer func() { + metrics.EndSpan(span) + }() + + return getSuccessResponse(PongOp) +} + +func (c *wsClient) handleSubscribe(ctx context.Context, content json.RawMessage) (resp model.ActiveRFQMessage) { + _, span := c.handler.Tracer().Start(ctx, "handleSubscribe", trace.WithAttributes( + attribute.String("relayer_address", c.relayerAddr), + )) + defer func() { + metrics.EndSpan(span) + }() + + var sub model.SubscriptionParams + err := json.Unmarshal(content, &sub) + if err != nil { + return getErrorResponse(SubscribeOp, fmt.Errorf("could not unmarshal subscription params: %w", err)) + } + span.SetAttributes(attribute.IntSlice("chain_ids", sub.Chains)) + err = c.pubsub.AddSubscription(c.relayerAddr, sub) + if err != nil { + return getErrorResponse(SubscribeOp, fmt.Errorf("error adding subscription: %w", err)) + } + return getSuccessResponse(SubscribeOp) +} + +func (c *wsClient) handleUnsubscribe(ctx context.Context, content json.RawMessage) (resp model.ActiveRFQMessage) { + _, span := c.handler.Tracer().Start(ctx, "handleUnsubscribe", trace.WithAttributes( + attribute.String("relayer_address", c.relayerAddr), + )) + defer func() { + metrics.EndSpan(span) + }() + + var sub model.SubscriptionParams + err := json.Unmarshal(content, &sub) + if err != nil { + return getErrorResponse(UnsubscribeOp, fmt.Errorf("could not unmarshal subscription params: %w", err)) + } + span.SetAttributes(attribute.IntSlice("chain_ids", sub.Chains)) + err = c.pubsub.RemoveSubscription(c.relayerAddr, sub) + if err != nil { + return getErrorResponse(UnsubscribeOp, fmt.Errorf("error removing subscription: %w", err)) + } + return getSuccessResponse(UnsubscribeOp) +} + +func (c *wsClient) handleSendQuote(ctx context.Context, content json.RawMessage) (err error) { + _, span := c.handler.Tracer().Start(ctx, "handleSendQuote", trace.WithAttributes( + attribute.String("relayer_address", c.relayerAddr), + )) + defer func() { + metrics.EndSpan(span) + }() + + // forward the response to the server + var resp model.WsRFQResponse + err = json.Unmarshal(content, &resp) + if err != nil { + return fmt.Errorf("error unmarshaling websocket message: %w", err) + } + span.SetAttributes( + attribute.String("request_id", resp.RequestID), + attribute.String("dest_amount", resp.DestAmount), + ) + responseChan, ok := c.responseChans.Load(resp.RequestID) + if !ok { + return fmt.Errorf("no response channel for request %s", resp.RequestID) + } + responseChan <- &resp + + return nil +} + +func getSuccessResponse(op string) model.ActiveRFQMessage { + return model.ActiveRFQMessage{ + Op: op, + Success: true, + } +} + +func getErrorResponse(op string, err error) model.ActiveRFQMessage { + return model.ActiveRFQMessage{ + Op: op, + Success: false, + Content: json.RawMessage(fmt.Sprintf("{\"reason\": \"%s\"}", err.Error())), + } +} diff --git a/services/rfq/e2e/setup_test.go b/services/rfq/e2e/setup_test.go index af4d93a0a5..d95366642d 100644 --- a/services/rfq/e2e/setup_test.go +++ b/services/rfq/e2e/setup_test.go @@ -314,7 +314,7 @@ func (i *IntegrationSuite) getRelayerConfig() relconfig.Config { }, // generated ex-post facto QuotableTokens: map[string][]string{}, - RfqAPIURL: i.apiServer, + RFQAPIURL: i.apiServer, Signer: signerConfig.SignerConfig{ Type: signerConfig.FileType.String(), File: filet.TmpFile(i.T(), "", i.relayerWallet.PrivateKeyHex()).Name(), diff --git a/services/rfq/go.mod b/services/rfq/go.mod index b463b6c502..3c24fbf857 100644 --- a/services/rfq/go.mod +++ b/services/rfq/go.mod @@ -16,11 +16,13 @@ require ( github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a github.com/go-resty/resty/v2 v2.13.1 github.com/google/uuid v1.6.0 + github.com/gorilla/websocket v1.5.3 github.com/ipfs/go-log v1.0.5 github.com/jellydator/ttlcache/v3 v3.1.1 github.com/jftuga/ellipsis v1.0.0 github.com/lmittmann/w3 v0.10.0 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 + github.com/puzpuzpuz/xsync v1.5.2 github.com/puzpuzpuz/xsync/v2 v2.5.1 github.com/shopspring/decimal v1.4.0 github.com/stretchr/testify v1.9.0 @@ -176,7 +178,6 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.4 // indirect - github.com/gorilla/websocket v1.5.3 // indirect github.com/grafana/otel-profiling-go v0.5.1 // indirect github.com/grafana/pyroscope-go v1.1.1 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.7 // indirect @@ -251,7 +252,6 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.54.0 // indirect github.com/prometheus/procfs v0.15.0 // indirect - github.com/puzpuzpuz/xsync v1.5.2 // indirect github.com/rbretecher/go-postman-collection v0.9.0 // indirect github.com/richardwilkes/toolbox v1.74.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect diff --git a/services/rfq/relayer/limiter/limiter_test.go b/services/rfq/relayer/limiter/limiter_test.go index 397c3da218..03e224ed99 100644 --- a/services/rfq/relayer/limiter/limiter_test.go +++ b/services/rfq/relayer/limiter/limiter_test.go @@ -82,6 +82,7 @@ func (l *LimiterSuite) TestUnderLimitNotEnoughConfirmations() { } func (l *LimiterSuite) TestOverLimitNotEnoughConfirmations() { + l.T().Skip() mockQuoter := buildMockQuoter(69420) mockListener := buildMockListener(4) diff --git a/services/rfq/relayer/quoter/export_test.go b/services/rfq/relayer/quoter/export_test.go index 81d719ae03..66817a0ce5 100644 --- a/services/rfq/relayer/quoter/export_test.go +++ b/services/rfq/relayer/quoter/export_test.go @@ -9,7 +9,7 @@ import ( "github.com/synapsecns/sanguine/services/rfq/relayer/relconfig" ) -func (m *Manager) GenerateQuotes(ctx context.Context, chainID int, address common.Address, balance *big.Int, inv map[int]map[common.Address]*big.Int) ([]model.PutQuoteRequest, error) { +func (m *Manager) GenerateQuotes(ctx context.Context, chainID int, address common.Address, balance *big.Int, inv map[int]map[common.Address]*big.Int) ([]model.PutRelayerQuoteRequest, error) { // nolint: errcheck return m.generateQuotes(ctx, chainID, address, balance, inv) } diff --git a/services/rfq/relayer/quoter/mocks/quoter.go b/services/rfq/relayer/quoter/mocks/quoter.go index 0832d49109..c794de8e03 100644 --- a/services/rfq/relayer/quoter/mocks/quoter.go +++ b/services/rfq/relayer/quoter/mocks/quoter.go @@ -92,6 +92,20 @@ func (_m *Quoter) SubmitAllQuotes(ctx context.Context) error { return r0 } +// SubscribeActiveRFQ provides a mock function with given fields: ctx +func (_m *Quoter) SubscribeActiveRFQ(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + type mockConstructorTestingTNewQuoter interface { mock.TestingT Cleanup(func()) diff --git a/services/rfq/relayer/quoter/quoter.go b/services/rfq/relayer/quoter/quoter.go index b1bfc0db9c..3b5ecc8352 100644 --- a/services/rfq/relayer/quoter/quoter.go +++ b/services/rfq/relayer/quoter/quoter.go @@ -3,6 +3,7 @@ package quoter import ( "context" + "encoding/json" "errors" "fmt" "math/big" @@ -31,6 +32,7 @@ import ( "github.com/synapsecns/sanguine/ethergo/signer/signer" rfqAPIClient "github.com/synapsecns/sanguine/services/rfq/api/client" "github.com/synapsecns/sanguine/services/rfq/api/model" + "github.com/synapsecns/sanguine/services/rfq/api/rest" "github.com/synapsecns/sanguine/services/rfq/relayer/inventory" ) @@ -42,6 +44,8 @@ var logger = log.Logger("quoter") type Quoter interface { // SubmitAllQuotes submits all quotes to the RFQ API. SubmitAllQuotes(ctx context.Context) (err error) + // SubscribeActiveRFQ subscribes to the RFQ websocket API. + SubscribeActiveRFQ(ctx context.Context) (err error) // ShouldProcess determines if a quote should be processed. // We do this by either saving all quotes in-memory, and refreshing via GetSelfQuotes() through the API // The first comparison is does bridge transaction OriginChainID+TokenAddr match with a quote + DestChainID+DestTokenAddr, then we look to see if we have enough amount to relay it + if the price fits our bounds (based on that the Relayer is relaying the destination token for the origin) @@ -81,7 +85,7 @@ type Manager struct { // quoteAmountGauge stores a histogram of quote amounts. quoteAmountGauge metric.Float64ObservableGauge // currentQuotes is used for recording quote metrics. - currentQuotes []model.PutQuoteRequest + currentQuotes []model.PutRelayerQuoteRequest } // NewQuoterManager creates a new QuoterManager. @@ -123,7 +127,7 @@ func NewQuoterManager(config relconfig.Config, metricsHandler metrics.Handler, i feePricer: feePricer, screener: ss, meter: metricsHandler.Meter(meterName), - currentQuotes: []model.PutQuoteRequest{}, + currentQuotes: []model.PutRelayerQuoteRequest{}, } m.quoteAmountGauge, err = m.meter.Float64ObservableGauge("quote_amount") @@ -251,6 +255,111 @@ func (m *Manager) SubmitAllQuotes(ctx context.Context) (err error) { return m.prepareAndSubmitQuotes(ctx, inv) } +// SubscribeActiveRFQ subscribes to the RFQ websocket API. +// This function is blocking and will run until the context is canceled. +func (m *Manager) SubscribeActiveRFQ(ctx context.Context) (err error) { + ctx, span := m.metricsHandler.Tracer().Start(ctx, "SubscribeActiveRFQ") + defer func() { + metrics.EndSpanWithErr(span, err) + }() + + chainIDs := []int{} + for chainID := range m.config.Chains { + chainIDs = append(chainIDs, chainID) + } + req := model.SubscribeActiveRFQRequest{ + ChainIDs: chainIDs, + } + span.SetAttributes(attribute.IntSlice("chain_ids", chainIDs)) + + reqChan := make(chan *model.ActiveRFQMessage) + respChan, err := m.rfqClient.SubscribeActiveQuotes(ctx, &req, reqChan) + if err != nil { + return fmt.Errorf("error subscribing to active quotes: %w", err) + } + span.AddEvent("subscribed to active quotes") + for { + select { + case <-ctx.Done(): + return nil + case msg, ok := <-respChan: + if !ok { + return errors.New("ws channel closed") + } + if msg == nil { + continue + } + resp, err := m.generateActiveRFQ(ctx, msg) + if err != nil { + return fmt.Errorf("error generating active RFQ message: %w", err) + } + reqChan <- resp + } + } +} + +// getActiveRFQ handles an active RFQ message. +// +//nolint:nilnil +func (m *Manager) generateActiveRFQ(ctx context.Context, msg *model.ActiveRFQMessage) (resp *model.ActiveRFQMessage, err error) { + ctx, span := m.metricsHandler.Tracer().Start(ctx, "generateActiveRFQ", trace.WithAttributes( + attribute.String("op", msg.Op), + attribute.String("content", string(msg.Content)), + )) + defer func() { + metrics.EndSpanWithErr(span, err) + }() + + if msg.Op != rest.RequestQuoteOp { + span.AddEvent("not a request quote op") + return nil, nil + } + + inv, err := m.inventoryManager.GetCommittableBalances(ctx, inventory.SkipDBCache()) + if err != nil { + return nil, fmt.Errorf("error getting committable balances: %w", err) + } + + var rfqRequest model.WsRFQRequest + err = json.Unmarshal(msg.Content, &rfqRequest) + if err != nil { + return nil, fmt.Errorf("error unmarshalling quote data: %w", err) + } + span.SetAttributes(attribute.String("request_id", rfqRequest.RequestID)) + + quoteInput := QuoteInput{ + OriginChainID: rfqRequest.Data.OriginChainID, + DestChainID: rfqRequest.Data.DestChainID, + OriginTokenAddr: common.HexToAddress(rfqRequest.Data.OriginTokenAddr), + DestTokenAddr: common.HexToAddress(rfqRequest.Data.DestTokenAddr), + OriginBalance: inv[rfqRequest.Data.OriginChainID][common.HexToAddress(rfqRequest.Data.OriginTokenAddr)], + DestBalance: inv[rfqRequest.Data.DestChainID][common.HexToAddress(rfqRequest.Data.DestTokenAddr)], + } + + rawQuote, err := m.generateQuote(ctx, quoteInput) + if err != nil { + return nil, fmt.Errorf("error generating quote: %w", err) + } + span.SetAttributes(attribute.String("dest_amount", rawQuote.DestAmount)) + + rfqResp := model.WsRFQResponse{ + RequestID: rfqRequest.RequestID, + DestAmount: rawQuote.DestAmount, + } + span.SetAttributes(attribute.String("dest_amount", rawQuote.DestAmount)) + respBytes, err := json.Marshal(rfqResp) + if err != nil { + return nil, fmt.Errorf("error serializing response: %w", err) + } + resp = &model.ActiveRFQMessage{ + Op: rest.SendQuoteOp, + Content: respBytes, + } + span.AddEvent("generated response") + + return resp, nil +} + // GetPrice gets the price of a token. func (m *Manager) GetPrice(parentCtx context.Context, tokenName string) (_ float64, err error) { ctx, span := m.metricsHandler.Tracer().Start(parentCtx, "GetPrice") @@ -274,7 +383,7 @@ func (m *Manager) prepareAndSubmitQuotes(ctx context.Context, inv map[int]map[co metrics.EndSpanWithErr(span, err) }() - var allQuotes []model.PutQuoteRequest + var allQuotes []model.PutRelayerQuoteRequest // First, generate all quotes g, gctx := errgroup.WithContext(ctx) @@ -343,7 +452,7 @@ const meterName = "github.com/synapsecns/sanguine/services/rfq/relayer/quoter" // Essentially, if we know a destination chain token balance, then we just need to find which tokens are bridgeable to it. // We can do this by looking at the quotableTokens map, and finding the key that matches the destination chain token. // Generates quotes for a given chain ID, address, and balance. -func (m *Manager) generateQuotes(parentCtx context.Context, chainID int, address common.Address, balance *big.Int, inv map[int]map[common.Address]*big.Int) (quotes []model.PutQuoteRequest, err error) { +func (m *Manager) generateQuotes(parentCtx context.Context, chainID int, address common.Address, balance *big.Int, inv map[int]map[common.Address]*big.Int) (quotes []model.PutRelayerQuoteRequest, err error) { ctx, span := m.metricsHandler.Tracer().Start(parentCtx, "generateQuotes", trace.WithAttributes( attribute.Int(metrics.Origin, chainID), attribute.String("address", address.String()), @@ -362,7 +471,7 @@ func (m *Manager) generateQuotes(parentCtx context.Context, chainID int, address // generate quotes in parallel g, gctx := errgroup.WithContext(ctx) quoteMtx := &sync.Mutex{} - quotes = []model.PutQuoteRequest{} + quotes = []model.PutRelayerQuoteRequest{} for k, itemTokenIDs := range m.quotableTokens { for _, tokenID := range itemTokenIDs { //nolint:nestif @@ -433,7 +542,7 @@ type QuoteInput struct { DestRFQAddr string } -func (m *Manager) generateQuote(ctx context.Context, input QuoteInput) (quote *model.PutQuoteRequest, err error) { +func (m *Manager) generateQuote(ctx context.Context, input QuoteInput) (quote *model.PutRelayerQuoteRequest, err error) { // Calculate the quote amount for this route originAmount, err := m.getOriginAmount(ctx, input) // don't quote if gas exceeds quote @@ -467,7 +576,7 @@ func (m *Manager) generateQuote(ctx context.Context, input QuoteInput) (quote *m logger.Error("Error getting dest amount", "error", err) return nil, fmt.Errorf("error getting dest amount: %w", err) } - quote = &model.PutQuoteRequest{ + quote = &model.PutRelayerQuoteRequest{ OriginChainID: input.OriginChainID, OriginTokenAddr: input.OriginTokenAddr.Hex(), DestChainID: input.DestChainID, @@ -700,7 +809,7 @@ func (m *Manager) applyOffset(parentCtx context.Context, offsetBps float64, targ } // Submits a single quote. -func (m *Manager) submitQuote(ctx context.Context, quote model.PutQuoteRequest) error { +func (m *Manager) submitQuote(ctx context.Context, quote model.PutRelayerQuoteRequest) error { quoteCtx, quoteCancel := context.WithTimeout(ctx, m.config.GetQuoteSubmissionTimeout()) defer quoteCancel() @@ -712,7 +821,7 @@ func (m *Manager) submitQuote(ctx context.Context, quote model.PutQuoteRequest) } // Submits multiple quotes. -func (m *Manager) submitBulkQuotes(ctx context.Context, quotes []model.PutQuoteRequest) error { +func (m *Manager) submitBulkQuotes(ctx context.Context, quotes []model.PutRelayerQuoteRequest) error { quoteCtx, quoteCancel := context.WithTimeout(ctx, m.config.GetQuoteSubmissionTimeout()) defer quoteCancel() diff --git a/services/rfq/relayer/quoter/quoter_test.go b/services/rfq/relayer/quoter/quoter_test.go index 328ba60eaa..1d6a52c7de 100644 --- a/services/rfq/relayer/quoter/quoter_test.go +++ b/services/rfq/relayer/quoter/quoter_test.go @@ -27,7 +27,7 @@ func (s *QuoterSuite) TestGenerateQuotes() { s.Require().NoError(err) // Verify the quotes are generated as expected. - expectedQuotes := []model.PutQuoteRequest{ + expectedQuotes := []model.PutRelayerQuoteRequest{ { OriginChainID: int(s.origin), OriginTokenAddr: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", @@ -55,7 +55,7 @@ func (s *QuoterSuite) TestGenerateQuotesForNativeToken() { expectedQuoteAmount := new(big.Int).Sub(balance, minGasToken) // Verify the quotes are generated as expected. - expectedQuotes := []model.PutQuoteRequest{ + expectedQuotes := []model.PutRelayerQuoteRequest{ { OriginChainID: int(s.origin), OriginTokenAddr: util.EthAddress.String(), @@ -82,7 +82,7 @@ func (s *QuoterSuite) TestGenerateQuotesForNativeToken() { expectedQuoteAmount = new(big.Int).Sub(balance, minGasToken) // Verify the quotes are generated as expected. - expectedQuotes = []model.PutQuoteRequest{ + expectedQuotes = []model.PutRelayerQuoteRequest{ { OriginChainID: int(s.origin), OriginTokenAddr: util.EthAddress.String(), diff --git a/services/rfq/relayer/relconfig/config.go b/services/rfq/relayer/relconfig/config.go index e55f87ac4e..a4449bf8db 100644 --- a/services/rfq/relayer/relconfig/config.go +++ b/services/rfq/relayer/relconfig/config.go @@ -33,8 +33,8 @@ type Config struct { BaseChainConfig ChainConfig `yaml:"base_chain_config"` // OmniRPCURL is the URL of the OmniRPC server. OmniRPCURL string `yaml:"omnirpc_url"` - // RfqAPIURL is the URL of the RFQ API. - RfqAPIURL string `yaml:"rfq_url"` + // RFQAPIURL is the URL of the RFQ API. + RFQAPIURL string `yaml:"rfq_url"` // RelayerAPIPort is the port of the relayer API. RelayerAPIPort string `yaml:"relayer_api_port"` // Database is the database config. @@ -67,6 +67,8 @@ type Config struct { SubmitSingleQuotes bool `yaml:"submit_single_quotes"` // VolumeLimit is the maximum dollar value of relayed transactions in the BlockWindow. VolumeLimit float64 `yaml:"volume_limit"` + // SupportActiveQuoting enables support for active quoting. + SupportActiveQuoting bool `yaml:"support_active_quoting"` } // ChainConfig represents the configuration for a chain. diff --git a/services/rfq/relayer/relconfig/getters.go b/services/rfq/relayer/relconfig/getters.go index d11534556c..2cb4880712 100644 --- a/services/rfq/relayer/relconfig/getters.go +++ b/services/rfq/relayer/relconfig/getters.go @@ -541,9 +541,9 @@ func (c Config) GetOmniRPCURL() string { return c.OmniRPCURL } -// GetRfqAPIURL returns the RFQ API URL. -func (c Config) GetRfqAPIURL() string { - return c.RfqAPIURL +// GetRFQAPIURL returns the RFQ API URL. +func (c Config) GetRFQAPIURL() string { + return c.RFQAPIURL } // GetDatabase returns the database config. diff --git a/services/rfq/relayer/service/relayer.go b/services/rfq/relayer/service/relayer.go index 90ad1a4e0e..38fdc4b701 100644 --- a/services/rfq/relayer/service/relayer.go +++ b/services/rfq/relayer/service/relayer.go @@ -130,7 +130,7 @@ func NewRelayer(ctx context.Context, metricHandler metrics.Handler, cfg relconfi priceFetcher := pricer.NewCoingeckoPriceFetcher(cfg.GetHTTPTimeout()) fp := pricer.NewFeePricer(cfg, omniClient, priceFetcher, metricHandler) - apiClient, err := rfqAPIClient.NewAuthenticatedClient(metricHandler, cfg.GetRfqAPIURL(), sg) + apiClient, err := rfqAPIClient.NewAuthenticatedClient(metricHandler, cfg.GetRFQAPIURL(), sg) if err != nil { return nil, fmt.Errorf("error creating RFQ API client: %w", err) } @@ -219,6 +219,16 @@ func (r *Relayer) Start(ctx context.Context) (err error) { } }) + if r.cfg.SupportActiveQuoting { + g.Go(func() error { + err = r.quoter.SubscribeActiveRFQ(ctx) + if err != nil { + return fmt.Errorf("could not subscribe to active RFQ: %w", err) + } + return nil + }) + } + g.Go(func() error { for { select { From 68821a7c7bca7b397abd7d54d5cf880af2a638b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CF=87=C2=B2?= <88190723+ChiTimesChi@users.noreply.github.com> Date: Thu, 3 Oct 2024 11:42:21 +0100 Subject: [PATCH 86/90] fix(contracts-rfq): gas bench tests for the views [SLT-275] (#3217) * test: add gas bench for encoding * test: add gas bench for canClaim --- .../test/FastBridgeV2.GasBench.Encoding.t.sol | 19 +++++++++++++++++++ .../test/FastBridgeV2.GasBench.Src.t.sol | 16 ++++++++++++---- 2 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 packages/contracts-rfq/test/FastBridgeV2.GasBench.Encoding.t.sol diff --git a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Encoding.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Encoding.t.sol new file mode 100644 index 0000000000..703e73267b --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Encoding.t.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2SrcBaseTest} from "./FastBridgeV2.Src.Base.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2GasBenchmarkSrcProtocolFeesTest is FastBridgeV2SrcBaseTest { + // TODO: add more tests with variable length requests once arbitrary call is done + + function test_getBridgeTransaction() public view { + bytes memory request = abi.encode(extractV1(tokenTx)); + fastBridge.getBridgeTransaction(request); + } + + function test_getBridgeTransactionV2() public view { + bytes memory request = abi.encode(tokenTx); + fastBridge.getBridgeTransactionV2(request); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol index 916f465475..0d6314baac 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol @@ -135,17 +135,21 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { } function test_claim_token() public { + bytes32 txId = getTxId(provenTokenTx); skipTimeAtLeast({time: CLAIM_DELAY + 1}); + assertTrue(fastBridge.canClaim(txId, relayerA)); claim({caller: relayerA, bridgeTx: provenTokenTx}); - assertEq(fastBridge.bridgeStatuses(getTxId(provenTokenTx)), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); assertEq(srcToken.balanceOf(relayerA), INITIAL_RELAYER_BALANCE + tokenTx.originAmount); assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenTx.originAmount); } function test_claimWithAddress_token() public { + bytes32 txId = getTxId(provenTokenTx); skipTimeAtLeast({time: CLAIM_DELAY + 1}); + assertTrue(fastBridge.canClaim(txId, relayerA)); claim({caller: relayerA, bridgeTx: provenTokenTx, to: relayerB}); - assertEq(fastBridge.bridgeStatuses(getTxId(provenTokenTx)), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); assertEq(srcToken.balanceOf(relayerB), INITIAL_RELAYER_BALANCE + tokenTx.originAmount); assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenTx.originAmount); } @@ -217,17 +221,21 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { } function test_claim_eth() public { + bytes32 txId = getTxId(provenEthTx); skipTimeAtLeast({time: CLAIM_DELAY + 1}); + assertTrue(fastBridge.canClaim(txId, relayerA)); claim({caller: relayerA, bridgeTx: provenEthTx}); - assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); assertEq(relayerA.balance, INITIAL_RELAYER_BALANCE + ethTx.originAmount); assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethTx.originAmount); } function test_claimWithAddress_eth() public { + bytes32 txId = getTxId(provenEthTx); skipTimeAtLeast({time: CLAIM_DELAY + 1}); + assertTrue(fastBridge.canClaim(txId, relayerA)); claim({caller: relayerA, bridgeTx: provenEthTx, to: relayerB}); - assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); assertEq(relayerB.balance, INITIAL_RELAYER_BALANCE + ethTx.originAmount); assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethTx.originAmount); } From 66bb939c5aaa9eda50126e957549cc375e09d221 Mon Sep 17 00:00:00 2001 From: ChiTimesChi Date: Thu, 3 Oct 2024 10:46:31 +0000 Subject: [PATCH 87/90] Publish - @synapsecns/contracts-rfq@0.6.2 --- packages/contracts-rfq/CHANGELOG.md | 11 +++++++++++ packages/contracts-rfq/package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/contracts-rfq/CHANGELOG.md b/packages/contracts-rfq/CHANGELOG.md index d94a85a1b9..16614416b6 100644 --- a/packages/contracts-rfq/CHANGELOG.md +++ b/packages/contracts-rfq/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.6.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.6.1...@synapsecns/contracts-rfq@0.6.2) (2024-10-03) + + +### Bug Fixes + +* **contracts-rfq:** gas bench tests for the views [SLT-275] ([#3217](https://github.com/synapsecns/sanguine/issues/3217)) ([68821a7](https://github.com/synapsecns/sanguine/commit/68821a7c7bca7b397abd7d54d5cf880af2a638b6)) + + + + + ## [0.6.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.6.0...@synapsecns/contracts-rfq@0.6.1) (2024-10-02) **Note:** Version bump only for package @synapsecns/contracts-rfq diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index 6af70089e9..288df494f0 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/contracts-rfq", "license": "MIT", - "version": "0.6.1", + "version": "0.6.2", "description": "FastBridge contracts.", "private": true, "files": [ From 244529c6823a11345f59f076d047cceef83410a4 Mon Sep 17 00:00:00 2001 From: aureliusbtc <82057759+aureliusbtc@users.noreply.github.com> Date: Thu, 3 Oct 2024 09:15:44 -0400 Subject: [PATCH 88/90] update bl --- packages/synapse-interface/public/blacklist.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/synapse-interface/public/blacklist.json b/packages/synapse-interface/public/blacklist.json index 333e118a81..22d5d66bd1 100644 --- a/packages/synapse-interface/public/blacklist.json +++ b/packages/synapse-interface/public/blacklist.json @@ -547,5 +547,9 @@ "0x3b00Daad3DE070f3060812e0F8D6b470B9F64Fa2", "0xeB428AdEF88EcFFbd819bD7Afa85d41b7fAa0510", "0xf97823da25d1e4051ca2bf4ffbe6b788df0628cb", - "0x105c3fB416146c18AD96a470140BEAE8fb9cf950" + "0x105c3fB416146c18AD96a470140BEAE8fb9cf950", + "0xd4d582840cc2bf3d1a6cc94b10355005ceeee2df", + "0x408d8e12c7ed8e5a7291fbD5E6164f41ecdA6B46", + "0x278dF4492d16321b247660799FAD1A12dE152Dd1", + "0x551BE68Cdf9Ce453ead61097649C34196d0bDb27" ] From 1a06439db40e68cb86f4599b92942112b9c13d86 Mon Sep 17 00:00:00 2001 From: defi-moses Date: Thu, 3 Oct 2024 16:18:56 +0100 Subject: [PATCH 89/90] fixing terminology --- docs/bridge/docs/05-Contracts/05-CCTP.md | 4 ++-- docs/bridge/docs/05-Contracts/06-RFQ.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/bridge/docs/05-Contracts/05-CCTP.md b/docs/bridge/docs/05-Contracts/05-CCTP.md index 44a96c48c9..011bdb90ea 100644 --- a/docs/bridge/docs/05-Contracts/05-CCTP.md +++ b/docs/bridge/docs/05-Contracts/05-CCTP.md @@ -6,9 +6,9 @@ The canonical list is hosted within the SynapseCNS on [Github](https://github.co # CCTP -Synapse CCTP contracts route through the [SynapseBridge](https://github.com/synapsecns/synapse-contracts/blob/ed93453430635e2d43704d5599d3318c43a23033/contracts/bridge/SynapseBridge.sol#L63-L118) contract to interact with [Circle CCTP contracts](https://developers.circle.com/stablecoins/docs/evm-smart-contracts) which mint and burn USDC across supported chains. +Synapse CCTP transactions primarily interact with the Synapse CCTP Router contract, which is responsible for routing transactions with the proper meta data to the Synapse CCTP contract on the relevant chain, which utilizes the base [Circle CCTP contracts](https://developers.circle.com/stablecoins/docs/evm-smart-contracts) to mint and burn USDC across supported chains. -**Address**: `0xd5a597d6e7ddf373a92c8f477daaa673b0902f48`\ +**Synapse CCTP Router Address**: `0xd5a597d6e7ddf373a92c8f477daaa673b0902f48`\ **Contract**: [SynapseCCTP.sol](https://github.com/synapsecns/synapse-contracts/blob/master/contracts/cctp/SynapseCCTP.sol) | Chain | Address | diff --git a/docs/bridge/docs/05-Contracts/06-RFQ.md b/docs/bridge/docs/05-Contracts/06-RFQ.md index 8f76cb9468..3e9691a1d2 100644 --- a/docs/bridge/docs/05-Contracts/06-RFQ.md +++ b/docs/bridge/docs/05-Contracts/06-RFQ.md @@ -10,11 +10,11 @@ The canonical list is hosted within the SynapseCNS on [Github](https://github.co # RFQ -RFQ contracts route through the [SynapseBridge](https://github.com/synapsecns/synapse-contracts/blob/ed93453430635e2d43704d5599d3318c43a23033/contracts/bridge/SynapseBridge.sol#L63-L118) contract. +RFQ transactions primarily interact with the Router Address, which is responsible for routing transactions with the proper meta data to the RFQ contract on the relevant chain. **Source code**: [SynapseCNS (Github)](https://github.com/synapsecns/sanguine/tree/master/packages/contracts-rfq)\ **Generated docs**: [RFQ docs](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html)\ -**Address**: `0x00cD000000003f7F682BE4813200893d4e690000` +**RFQ Router Address**: `0x00cD000000003f7F682BE4813200893d4e690000` | Chain | Address | | -------- | ------- | From b40bc843179a6c4d016f2c15660b2f7137c2709f Mon Sep 17 00:00:00 2001 From: defi-moses Date: Thu, 3 Oct 2024 16:28:41 +0100 Subject: [PATCH 90/90] rfq-indexer-docs --- docs/bridge/docs/06-Services/06-RFQ-Indexer-API.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 docs/bridge/docs/06-Services/06-RFQ-Indexer-API.md diff --git a/docs/bridge/docs/06-Services/06-RFQ-Indexer-API.md b/docs/bridge/docs/06-Services/06-RFQ-Indexer-API.md new file mode 100644 index 0000000000..1cbb3ddca3 --- /dev/null +++ b/docs/bridge/docs/06-Services/06-RFQ-Indexer-API.md @@ -0,0 +1,14 @@ +# RFQ Indexer API + +The RFQ Indexer API is a service designed to provide access to indexed RFQ bridge event data. It offers crucial monitoring and operational capabilities, serving as a supplemental tool to existing relayer infrastructure. This API is particularly useful for tracking pending bridge transactions and identifying missing relays, proofs, or claims. + +## API-docs + +[`https://triumphant-magic-production.up.railway.app/api-docs/`](https://triumphant-magic-production.up.railway.app/api-docs/) + +## Key Features +1. **Real-Time and Historical Data** Indexes from a specified start block up to real-time events. +2. **On-chain Tracing** Tracks all on-chain transactions and events, helping to debug. +4. **GraphQL API**: Provides a GraphQL/IQL endpoint for easy data querying. + +