services/explorer/contracts/cctp/cctp.contractinfo.json
{"solidity/SynapseCCTPV1_flat.sol:ActionLib":{"code":"0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220c1b14cecff239984d0910096ae2c879d0b6aa72288ea9d4d1cbfffa7a2d2e6ef64736f6c634300080d0033","runtime-code":"0x73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220c1b14cecff239984d0910096ae2c879d0b6aa72288ea9d4d1cbfffa7a2d2e6ef64736f6c634300080d0033","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"5746:808:0:-:0;;;;;;;;;;;;;;;-1:-1:-1;;;5746:808:0;;;;;;;;;;;;;;;;;","srcMapRuntime":"5746:808:0:-:0;;;;;;;;","abiDefinition":[],"userDoc":{"kind":"user","methods":{},"notice":"Library for dealing with bit masks which describe what set of Actions is available.","version":1},"developerDoc":{"kind":"dev","methods":{},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"notice\":\"Library for dealing with bit masks which describe what set of Actions is available.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"ActionLib\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{}},"solidity/SynapseCCTPV1_flat.sol:Address":{"code":"0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220655c6d0fdf779c53ad8bbc9d91838f1d883314cce982482dabe34d66730ec6af64736f6c634300080d0033","runtime-code":"0x73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220655c6d0fdf779c53ad8bbc9d91838f1d883314cce982482dabe34d66730ec6af64736f6c634300080d0033","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"6695:8061:0:-:0;;;;;;;;;;;;;;;-1:-1:-1;;;6695:8061:0;;;;;;;;;;;;;;;;;","srcMapRuntime":"6695:8061:0:-:0;;;;;;;;","abiDefinition":[],"userDoc":{"kind":"user","methods":{},"version":1},"developerDoc":{"details":"Collection of functions related to the address type","kind":"dev","methods":{},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"details\":\"Collection of functions related to the address type\",\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"Address\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{}},"solidity/SynapseCCTPV1_flat.sol:Context":{"code":"0x","runtime-code":"0x","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"","srcMapRuntime":"","abiDefinition":[],"userDoc":{"kind":"user","methods":{},"version":1},"developerDoc":{"details":"Provides information about the current execution context, including the sender of the transaction and its data. While these are generally available via msg.sender and msg.data, they should not be accessed in such a direct manner, since when dealing with meta-transactions the account sending and paying for execution may not be the actual sender (as far as an application is concerned). This contract is only required for intermediate, library-like contracts.","kind":"dev","methods":{},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"details\":\"Provides information about the current execution context, including the sender of the transaction and its data. While these are generally available via msg.sender and msg.data, they should not be accessed in such a direct manner, since when dealing with meta-transactions the account sending and paying for execution may not be the actual sender (as far as an application is concerned). This contract is only required for intermediate, library-like contracts.\",\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"Context\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{}},"solidity/SynapseCCTPV1_flat.sol:EnumerableSet":{"code":"0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220f1e69d77df4221ee9f4a6d53f3246fc1c7af269baad02b7bcdcafb65f8b5382b64736f6c634300080d0033","runtime-code":"0x73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220f1e69d77df4221ee9f4a6d53f3246fc1c7af269baad02b7bcdcafb65f8b5382b64736f6c634300080d0033","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"31227:11368:0:-:0;;;;;;;;;;;;;;;-1:-1:-1;;;31227:11368:0;;;;;;;;;;;;;;;;;","srcMapRuntime":"31227:11368:0:-:0;;;;;;;;","abiDefinition":[],"userDoc":{"kind":"user","methods":{},"version":1},"developerDoc":{"details":"Library for managing https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive types. Sets have the following properties: - Elements are added, removed, and checked for existence in constant time (O(1)). - Elements are enumerated in O(n). No guarantees are made on the ordering. ``` contract Example { // Add the library methods using EnumerableSet for EnumerableSet.AddressSet; // Declare a set state variable EnumerableSet.AddressSet private mySet; } ``` As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) and `uint256` (`UintSet`) are supported.","kind":"dev","methods":{},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"details\":\"Library for managing https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive types. Sets have the following properties: - Elements are added, removed, and checked for existence in constant time (O(1)). - Elements are enumerated in O(n). No guarantees are made on the ordering. ``` contract Example { // Add the library methods using EnumerableSet for EnumerableSet.AddressSet; // Declare a set state variable EnumerableSet.AddressSet private mySet; } ``` As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) and `uint256` (`UintSet`) are supported.\",\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"EnumerableSet\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{}},"solidity/SynapseCCTPV1_flat.sol:IDefaultPool":{"code":"0x","runtime-code":"0x","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"","srcMapRuntime":"","abiDefinition":[{"inputs":[{"internalType":"uint8","name":"tokenIndexFrom","type":"uint8"},{"internalType":"uint8","name":"tokenIndexTo","type":"uint8"},{"internalType":"uint256","name":"dx","type":"uint256"}],"name":"calculateSwap","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"index","type":"uint8"}],"name":"getToken","outputs":[{"internalType":"address","name":"token","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"tokenIndexFrom","type":"uint8"},{"internalType":"uint8","name":"tokenIndexTo","type":"uint8"},{"internalType":"uint256","name":"dx","type":"uint256"},{"internalType":"uint256","name":"minDy","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swap","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}],"userDoc":{"kind":"user","methods":{},"version":1},"developerDoc":{"kind":"dev","methods":{},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"tokenIndexFrom\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"tokenIndexTo\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"dx\",\"type\":\"uint256\"}],\"name\":\"calculateSwap\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amountOut\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"}],\"name\":\"getToken\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"tokenIndexFrom\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"tokenIndexTo\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"dx\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minDy\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"}],\"name\":\"swap\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amountOut\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"IDefaultPool\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{"calculateSwap(uint8,uint8,uint256)":"a95b089f","getToken(uint8)":"82b86600","swap(uint8,uint8,uint256,uint256,uint256)":"91695586"}},"solidity/SynapseCCTPV1_flat.sol:IERC20":{"code":"0x","runtime-code":"0x","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"","srcMapRuntime":"","abiDefinition":[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}],"userDoc":{"kind":"user","methods":{},"version":1},"developerDoc":{"details":"Interface of the ERC20 standard as defined in the EIP.","events":{"Approval(address,address,uint256)":{"details":"Emitted when the allowance of a `spender` for an `owner` is set by a call to {approve}. `value` is the new allowance."},"Transfer(address,address,uint256)":{"details":"Emitted when `value` tokens are moved from one account (`from`) to another (`to`). Note that `value` may be zero."}},"kind":"dev","methods":{"allowance(address,address)":{"details":"Returns the remaining number of tokens that `spender` will be allowed to spend on behalf of `owner` through {transferFrom}. This is zero by default. This value changes when {approve} or {transferFrom} are called."},"approve(address,uint256)":{"details":"Sets `amount` as the allowance of `spender` over the caller's tokens. Returns a boolean value indicating whether the operation succeeded. IMPORTANT: Beware that changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 Emits an {Approval} event."},"balanceOf(address)":{"details":"Returns the amount of tokens owned by `account`."},"totalSupply()":{"details":"Returns the amount of tokens in existence."},"transfer(address,uint256)":{"details":"Moves `amount` tokens from the caller's account to `to`. Returns a boolean value indicating whether the operation succeeded. Emits a {Transfer} event."},"transferFrom(address,address,uint256)":{"details":"Moves `amount` tokens from `from` to `to` using the allowance mechanism. `amount` is then deducted from the caller's allowance. Returns a boolean value indicating whether the operation succeeded. Emits a {Transfer} event."}},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"Interface of the ERC20 standard as defined in the EIP.\",\"events\":{\"Approval(address,address,uint256)\":{\"details\":\"Emitted when the allowance of a `spender` for an `owner` is set by a call to {approve}. `value` is the new allowance.\"},\"Transfer(address,address,uint256)\":{\"details\":\"Emitted when `value` tokens are moved from one account (`from`) to another (`to`). Note that `value` may be zero.\"}},\"kind\":\"dev\",\"methods\":{\"allowance(address,address)\":{\"details\":\"Returns the remaining number of tokens that `spender` will be allowed to spend on behalf of `owner` through {transferFrom}. This is zero by default. This value changes when {approve} or {transferFrom} are called.\"},\"approve(address,uint256)\":{\"details\":\"Sets `amount` as the allowance of `spender` over the caller's tokens. Returns a boolean value indicating whether the operation succeeded. IMPORTANT: Beware that changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 Emits an {Approval} event.\"},\"balanceOf(address)\":{\"details\":\"Returns the amount of tokens owned by `account`.\"},\"totalSupply()\":{\"details\":\"Returns the amount of tokens in existence.\"},\"transfer(address,uint256)\":{\"details\":\"Moves `amount` tokens from the caller's account to `to`. Returns a boolean value indicating whether the operation succeeded. Emits a {Transfer} event.\"},\"transferFrom(address,address,uint256)\":{\"details\":\"Moves `amount` tokens from `from` to `to` using the allowance mechanism. `amount` is then deducted from the caller's allowance. Returns a boolean value indicating whether the operation succeeded. Emits a {Transfer} event.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"IERC20\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{"allowance(address,address)":"dd62ed3e","approve(address,uint256)":"095ea7b3","balanceOf(address)":"70a08231","totalSupply()":"18160ddd","transfer(address,uint256)":"a9059cbb","transferFrom(address,address,uint256)":"23b872dd"}},"solidity/SynapseCCTPV1_flat.sol:IMessageTransmitter":{"code":"0x","runtime-code":"0x","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"","srcMapRuntime":"","abiDefinition":[{"inputs":[],"name":"localDomain","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextAvailableNonce","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"receiveMessage","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"internalType":"bytes32","name":"recipient","type":"bytes32"},{"internalType":"bytes32","name":"destinationCaller","type":"bytes32"},{"internalType":"bytes","name":"messageBody","type":"bytes"}],"name":"sendMessageWithCaller","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"nonpayable","type":"function"}],"userDoc":{"kind":"user","methods":{"receiveMessage(bytes,bytes)":{"notice":"Receives an incoming message, validating the header and passing the body to application-specific handler."},"sendMessageWithCaller(uint32,bytes32,bytes32,bytes)":{"notice":"Sends an outgoing message from the source domain, with a specified caller on the destination domain."}},"version":1},"developerDoc":{"kind":"dev","methods":{"receiveMessage(bytes,bytes)":{"params":{"message":"The message raw bytes","signature":"The message signature"},"returns":{"success":"bool, true if successful"}},"sendMessageWithCaller(uint32,bytes32,bytes32,bytes)":{"details":"Increment nonce, format the message, and emit `MessageSent` event with message information. WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible to broadcast the message on the destination domain. This is an advanced feature, and the standard sendMessage() should be preferred for use cases where a specific destination caller is not required.","params":{"destinationCaller":"caller on the destination domain, as bytes32","destinationDomain":"Domain of destination chain","messageBody":"Raw bytes content of message","recipient":"Address of message recipient on destination domain as bytes32"},"returns":{"_0":"nonce reserved by message"}}},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"localDomain\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nextAvailableNonce\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"receiveMessage\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"destinationDomain\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"destinationCaller\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"messageBody\",\"type\":\"bytes\"}],\"name\":\"sendMessageWithCaller\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"receiveMessage(bytes,bytes)\":{\"params\":{\"message\":\"The message raw bytes\",\"signature\":\"The message signature\"},\"returns\":{\"success\":\"bool, true if successful\"}},\"sendMessageWithCaller(uint32,bytes32,bytes32,bytes)\":{\"details\":\"Increment nonce, format the message, and emit `MessageSent` event with message information. WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible to broadcast the message on the destination domain. This is an advanced feature, and the standard sendMessage() should be preferred for use cases where a specific destination caller is not required.\",\"params\":{\"destinationCaller\":\"caller on the destination domain, as bytes32\",\"destinationDomain\":\"Domain of destination chain\",\"messageBody\":\"Raw bytes content of message\",\"recipient\":\"Address of message recipient on destination domain as bytes32\"},\"returns\":{\"_0\":\"nonce reserved by message\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"receiveMessage(bytes,bytes)\":{\"notice\":\"Receives an incoming message, validating the header and passing the body to application-specific handler.\"},\"sendMessageWithCaller(uint32,bytes32,bytes32,bytes)\":{\"notice\":\"Sends an outgoing message from the source domain, with a specified caller on the destination domain.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"IMessageTransmitter\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{"localDomain()":"8d3638f4","nextAvailableNonce()":"8371744e","receiveMessage(bytes,bytes)":"57ecfd28","sendMessageWithCaller(uint32,bytes32,bytes32,bytes)":"f7259a75"}},"solidity/SynapseCCTPV1_flat.sol:ISynapseCCTP":{"code":"0x","runtime-code":"0x","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"","srcMapRuntime":"","abiDefinition":[{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"circleTokenPool","outputs":[{"internalType":"address","name":"pool","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint32","name":"requestVersion","type":"uint32"},{"internalType":"bytes","name":"formattedRequest","type":"bytes"}],"name":"receiveCircleToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"burnToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint32","name":"requestVersion","type":"uint32"},{"internalType":"bytes","name":"swapParams","type":"bytes"}],"name":"sendCircleToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tokenMessenger","outputs":[{"internalType":"contract ITokenMessenger","name":"","type":"address"}],"stateMutability":"view","type":"function"}],"userDoc":{"kind":"user","methods":{"circleTokenPool(address)":{"notice":"Returns the whitelisted liquidity pool for a given Circle token."},"receiveCircleToken(bytes,bytes,uint32,bytes)":{"notice":"Receive Circle token supported by CCTP with the request for the action to take."},"sendCircleToken(address,uint256,address,uint256,uint32,bytes)":{"notice":"Send a Circle token supported by CCTP to a given chain with the request for the action to take on the destination chain."},"tokenMessenger()":{"notice":"Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens."}},"version":1},"developerDoc":{"kind":"dev","methods":{"circleTokenPool(address)":{"details":"Returns address(0) if the token bridge+swap is not supported."},"receiveCircleToken(bytes,bytes,uint32,bytes)":{"details":"The request is a bytes array containing information about the end recipient of the tokens, as well as an optional swap action to take on this chain.The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function, or the call will revert.","params":{"formattedRequest":"Formatted request for the action to take on this chain","message":"Message raw bytes emitted by CCTP MessageTransmitter on origin chain","requestVersion":"Version of the request format","signature":"Circle's attestation for the message obtained from Circle's API"}},"sendCircleToken(address,uint256,address,uint256,uint32,bytes)":{"details":"The request is a bytes array containing information about the end recipient of the tokens, as well as an optional swap action to take on the destination chain. `chainId` refers to value from EIP-155 (block.chainid).","params":{"amount":"Amount of tokens to burn","burnToken":"Address of Circle token to burn","chainId":"Chain ID of the destination chain","recipient":"Recipient of the tokens on destination chain","requestVersion":"Version of the request format","swapParams":"Swap parameters for the action to take on the destination chain (could be empty)"}}},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"circleTokenPool\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"pool\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"},{\"internalType\":\"uint32\",\"name\":\"requestVersion\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"formattedRequest\",\"type\":\"bytes\"}],\"name\":\"receiveCircleToken\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"burnToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"requestVersion\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"swapParams\",\"type\":\"bytes\"}],\"name\":\"sendCircleToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"tokenMessenger\",\"outputs\":[{\"internalType\":\"contract ITokenMessenger\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"circleTokenPool(address)\":{\"details\":\"Returns address(0) if the token bridge+swap is not supported.\"},\"receiveCircleToken(bytes,bytes,uint32,bytes)\":{\"details\":\"The request is a bytes array containing information about the end recipient of the tokens, as well as an optional swap action to take on this chain.The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function, or the call will revert.\",\"params\":{\"formattedRequest\":\"Formatted request for the action to take on this chain\",\"message\":\"Message raw bytes emitted by CCTP MessageTransmitter on origin chain\",\"requestVersion\":\"Version of the request format\",\"signature\":\"Circle's attestation for the message obtained from Circle's API\"}},\"sendCircleToken(address,uint256,address,uint256,uint32,bytes)\":{\"details\":\"The request is a bytes array containing information about the end recipient of the tokens, as well as an optional swap action to take on the destination chain. `chainId` refers to value from EIP-155 (block.chainid).\",\"params\":{\"amount\":\"Amount of tokens to burn\",\"burnToken\":\"Address of Circle token to burn\",\"chainId\":\"Chain ID of the destination chain\",\"recipient\":\"Recipient of the tokens on destination chain\",\"requestVersion\":\"Version of the request format\",\"swapParams\":\"Swap parameters for the action to take on the destination chain (could be empty)\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"circleTokenPool(address)\":{\"notice\":\"Returns the whitelisted liquidity pool for a given Circle token.\"},\"receiveCircleToken(bytes,bytes,uint32,bytes)\":{\"notice\":\"Receive Circle token supported by CCTP with the request for the action to take.\"},\"sendCircleToken(address,uint256,address,uint256,uint32,bytes)\":{\"notice\":\"Send a Circle token supported by CCTP to a given chain with the request for the action to take on the destination chain.\"},\"tokenMessenger()\":{\"notice\":\"Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"ISynapseCCTP\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{"circleTokenPool(address)":"a4b1d034","receiveCircleToken(bytes,bytes,uint32,bytes)":"4a5ae51d","sendCircleToken(address,uint256,address,uint256,uint32,bytes)":"304ddb4c","tokenMessenger()":"46117830"}},"solidity/SynapseCCTPV1_flat.sol:ISynapseCCTPFees":{"code":"0x","runtime-code":"0x","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"","srcMapRuntime":"","abiDefinition":[{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"isSwap","type":"bool"}],"name":"calculateFeeAmount","outputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"feeStructures","outputs":[{"internalType":"uint40","name":"relayerFee","type":"uint40"},{"internalType":"uint72","name":"minBaseFee","type":"uint72"},{"internalType":"uint72","name":"minSwapFee","type":"uint72"},{"internalType":"uint72","name":"maxFee","type":"uint72"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBridgeTokens","outputs":[{"components":[{"internalType":"string","name":"symbol","type":"string"},{"internalType":"address","name":"token","type":"address"}],"internalType":"struct BridgeToken[]","name":"bridgeTokens","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"symbol","type":"string"}],"name":"symbolToToken","outputs":[{"internalType":"address","name":"token","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"tokenToSymbol","outputs":[{"internalType":"string","name":"symbol","type":"string"}],"stateMutability":"view","type":"function"}],"userDoc":{"kind":"user","methods":{"calculateFeeAmount(address,uint256,bool)":{"notice":"Calculates the fee amount for bridging a token to this chain using CCTP."},"feeStructures(address)":{"notice":"Gets the fee structure for bridging a token to this chain."},"getBridgeTokens()":{"notice":"Returns the list of all supported bridge tokens and their symbols."},"symbolToToken(string)":{"notice":"Returns the address of the CCTP token for a given symbol."},"tokenToSymbol(address)":{"notice":"Returns the symbol of a given CCTP token."}},"version":1},"developerDoc":{"kind":"dev","methods":{"calculateFeeAmount(address,uint256,bool)":{"details":"Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.","params":{"amount":"Amount of the Circle tokens to be bridged to this chain","isSwap":"Whether the request is a swap request","token":"Address of the Circle token"},"returns":{"fee":" Fee amount"}},"feeStructures(address)":{"details":"Will return 0 for all fields if the token is not supported.","params":{"token":"Address of the Circle token"},"returns":{"maxFee":" Maximum fee for bridging a token to this chain","minBaseFee":" Minimum fee for bridging a token to this chain using a base request","minSwapFee":" Minimum fee for bridging a token to this chain using a swap request","relayerFee":" Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`"}},"symbolToToken(string)":{"details":"Will return address(0) if the token is not supported."},"tokenToSymbol(address)":{"details":"Will return empty string if the token is not supported."}},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isSwap\",\"type\":\"bool\"}],\"name\":\"calculateFeeAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"feeStructures\",\"outputs\":[{\"internalType\":\"uint40\",\"name\":\"relayerFee\",\"type\":\"uint40\"},{\"internalType\":\"uint72\",\"name\":\"minBaseFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"minSwapFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"maxFee\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBridgeTokens\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"symbol\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"internalType\":\"struct BridgeToken[]\",\"name\":\"bridgeTokens\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"symbol\",\"type\":\"string\"}],\"name\":\"symbolToToken\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"tokenToSymbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"symbol\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"calculateFeeAmount(address,uint256,bool)\":{\"details\":\"Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\",\"params\":{\"amount\":\"Amount of the Circle tokens to be bridged to this chain\",\"isSwap\":\"Whether the request is a swap request\",\"token\":\"Address of the Circle token\"},\"returns\":{\"fee\":\" Fee amount\"}},\"feeStructures(address)\":{\"details\":\"Will return 0 for all fields if the token is not supported.\",\"params\":{\"token\":\"Address of the Circle token\"},\"returns\":{\"maxFee\":\" Maximum fee for bridging a token to this chain\",\"minBaseFee\":\" Minimum fee for bridging a token to this chain using a base request\",\"minSwapFee\":\" Minimum fee for bridging a token to this chain using a swap request\",\"relayerFee\":\" Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\"}},\"symbolToToken(string)\":{\"details\":\"Will return address(0) if the token is not supported.\"},\"tokenToSymbol(address)\":{\"details\":\"Will return empty string if the token is not supported.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"calculateFeeAmount(address,uint256,bool)\":{\"notice\":\"Calculates the fee amount for bridging a token to this chain using CCTP.\"},\"feeStructures(address)\":{\"notice\":\"Gets the fee structure for bridging a token to this chain.\"},\"getBridgeTokens()\":{\"notice\":\"Returns the list of all supported bridge tokens and their symbols.\"},\"symbolToToken(string)\":{\"notice\":\"Returns the address of the CCTP token for a given symbol.\"},\"tokenToSymbol(address)\":{\"notice\":\"Returns the symbol of a given CCTP token.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"ISynapseCCTPFees\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{"calculateFeeAmount(address,uint256,bool)":"0d25aafe","feeStructures(address)":"dc72495b","getBridgeTokens()":"9c1d060e","symbolToToken(string)":"a5bc29c2","tokenToSymbol(address)":"0ba36121"}},"solidity/SynapseCCTPV1_flat.sol:ITokenMessenger":{"code":"0x","runtime-code":"0x","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"","srcMapRuntime":"","abiDefinition":[{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"internalType":"bytes32","name":"mintRecipient","type":"bytes32"},{"internalType":"address","name":"burnToken","type":"address"},{"internalType":"bytes32","name":"destinationCaller","type":"bytes32"}],"name":"depositForBurnWithCaller","outputs":[{"internalType":"uint64","name":"nonce","type":"uint64"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"remoteDomain","type":"uint32"},{"internalType":"bytes32","name":"sender","type":"bytes32"},{"internalType":"bytes","name":"messageBody","type":"bytes"}],"name":"handleReceiveMessage","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"localMessageTransmitter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"localMinter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}],"userDoc":{"kind":"user","methods":{"depositForBurnWithCaller(uint256,uint32,bytes32,address,bytes32)":{"notice":"Deposits and burns tokens from sender to be minted on destination domain. The mint on the destination domain must be called by `destinationCaller`. WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible to broadcast the message on the destination domain. This is an advanced feature, and the standard depositForBurn() should be preferred for use cases where a specific destination caller is not required. Emits a `DepositForBurn` event."},"handleReceiveMessage(uint32,bytes32,bytes)":{"notice":"Handles an incoming message received by the local MessageTransmitter, and takes the appropriate action. For a burn message, mints the associated token to the requested recipient on the local domain."}},"version":1},"developerDoc":{"kind":"dev","methods":{"depositForBurnWithCaller(uint256,uint32,bytes32,address,bytes32)":{"details":"reverts if: - given destinationCaller is zero address - given burnToken is not supported - given destinationDomain has no TokenMessenger registered - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance to this contract is less than `amount`. - burn() reverts. For example, if `amount` is 0. - MessageTransmitter returns false or reverts.","params":{"amount":"amount of tokens to burn","burnToken":"address of contract to burn deposited tokens, on local domain","destinationCaller":"caller on the destination domain, as bytes32","destinationDomain":"destination domain","mintRecipient":"address of mint recipient on destination domain"},"returns":{"nonce":"unique nonce reserved by message"}},"handleReceiveMessage(uint32,bytes32,bytes)":{"details":"Validates the local sender is the local MessageTransmitter, and the remote sender is a registered remote TokenMessenger for `remoteDomain`.","params":{"messageBody":"The message body bytes.","remoteDomain":"The domain where the message originated from.","sender":"The sender of the message (remote TokenMessenger)."},"returns":{"success":"Bool, true if successful."}}},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"destinationDomain\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"mintRecipient\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"burnToken\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"destinationCaller\",\"type\":\"bytes32\"}],\"name\":\"depositForBurnWithCaller\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"remoteDomain\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"sender\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"messageBody\",\"type\":\"bytes\"}],\"name\":\"handleReceiveMessage\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"localMessageTransmitter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"localMinter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"depositForBurnWithCaller(uint256,uint32,bytes32,address,bytes32)\":{\"details\":\"reverts if: - given destinationCaller is zero address - given burnToken is not supported - given destinationDomain has no TokenMessenger registered - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance to this contract is less than `amount`. - burn() reverts. For example, if `amount` is 0. - MessageTransmitter returns false or reverts.\",\"params\":{\"amount\":\"amount of tokens to burn\",\"burnToken\":\"address of contract to burn deposited tokens, on local domain\",\"destinationCaller\":\"caller on the destination domain, as bytes32\",\"destinationDomain\":\"destination domain\",\"mintRecipient\":\"address of mint recipient on destination domain\"},\"returns\":{\"nonce\":\"unique nonce reserved by message\"}},\"handleReceiveMessage(uint32,bytes32,bytes)\":{\"details\":\"Validates the local sender is the local MessageTransmitter, and the remote sender is a registered remote TokenMessenger for `remoteDomain`.\",\"params\":{\"messageBody\":\"The message body bytes.\",\"remoteDomain\":\"The domain where the message originated from.\",\"sender\":\"The sender of the message (remote TokenMessenger).\"},\"returns\":{\"success\":\"Bool, true if successful.\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"depositForBurnWithCaller(uint256,uint32,bytes32,address,bytes32)\":{\"notice\":\"Deposits and burns tokens from sender to be minted on destination domain. The mint on the destination domain must be called by `destinationCaller`. WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible to broadcast the message on the destination domain. This is an advanced feature, and the standard depositForBurn() should be preferred for use cases where a specific destination caller is not required. Emits a `DepositForBurn` event.\"},\"handleReceiveMessage(uint32,bytes32,bytes)\":{\"notice\":\"Handles an incoming message received by the local MessageTransmitter, and takes the appropriate action. For a burn message, mints the associated token to the requested recipient on the local domain.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"ITokenMessenger\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{"depositForBurnWithCaller(uint256,uint32,bytes32,address,bytes32)":"f856ddb6","handleReceiveMessage(uint32,bytes32,bytes)":"96abeb70","localMessageTransmitter()":"2c121921","localMinter()":"cb75c11c"}},"solidity/SynapseCCTPV1_flat.sol:ITokenMinter":{"code":"0x","runtime-code":"0x","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"","srcMapRuntime":"","abiDefinition":[{"inputs":[{"internalType":"address","name":"burnToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"burnLimitsPerMessage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"remoteDomain","type":"uint32"},{"internalType":"bytes32","name":"remoteToken","type":"bytes32"}],"name":"getLocalToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"sourceDomain","type":"uint32"},{"internalType":"bytes32","name":"burnToken","type":"bytes32"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[{"internalType":"address","name":"mintToken","type":"address"}],"stateMutability":"nonpayable","type":"function"}],"userDoc":{"kind":"user","methods":{"burn(address,uint256)":{"notice":"Burn tokens owned by this ITokenMinter."},"getLocalToken(uint32,bytes32)":{"notice":"Get the local token associated with the given remote domain and token."},"mint(uint32,bytes32,address,uint256)":{"notice":"Mints `amount` of local tokens corresponding to the given (`sourceDomain`, `burnToken`) pair, to `to` address."}},"version":1},"developerDoc":{"kind":"dev","methods":{"burn(address,uint256)":{"params":{"amount":"amount of tokens to burn. Must be less than or equal to this ITokenMinter's account balance of the given `_burnToken`.","burnToken":"burnable token."}},"getLocalToken(uint32,bytes32)":{"params":{"remoteDomain":"Remote domain","remoteToken":"Remote token"},"returns":{"_0":"local token address"}},"mint(uint32,bytes32,address,uint256)":{"details":"reverts if the (`sourceDomain`, `burnToken`) pair does not map to a nonzero local token address. This mapping can be queried using getLocalToken().","params":{"amount":"Amount of tokens to mint. Must be less than or equal to the minterAllowance of this TokenMinter for given `_mintToken`.","burnToken":"Burned token address as bytes32.","sourceDomain":"Source domain where `burnToken` was burned.","to":"Address to receive minted tokens, corresponding to `burnToken`, on this domain."},"returns":{"mintToken":"token minted."}}},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"burnToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"burnLimitsPerMessage\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"remoteDomain\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"remoteToken\",\"type\":\"bytes32\"}],\"name\":\"getLocalToken\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"sourceDomain\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"burnToken\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"mintToken\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"burn(address,uint256)\":{\"params\":{\"amount\":\"amount of tokens to burn. Must be less than or equal to this ITokenMinter's account balance of the given `_burnToken`.\",\"burnToken\":\"burnable token.\"}},\"getLocalToken(uint32,bytes32)\":{\"params\":{\"remoteDomain\":\"Remote domain\",\"remoteToken\":\"Remote token\"},\"returns\":{\"_0\":\"local token address\"}},\"mint(uint32,bytes32,address,uint256)\":{\"details\":\"reverts if the (`sourceDomain`, `burnToken`) pair does not map to a nonzero local token address. This mapping can be queried using getLocalToken().\",\"params\":{\"amount\":\"Amount of tokens to mint. Must be less than or equal to the minterAllowance of this TokenMinter for given `_mintToken`.\",\"burnToken\":\"Burned token address as bytes32.\",\"sourceDomain\":\"Source domain where `burnToken` was burned.\",\"to\":\"Address to receive minted tokens, corresponding to `burnToken`, on this domain.\"},\"returns\":{\"mintToken\":\"token minted.\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"burn(address,uint256)\":{\"notice\":\"Burn tokens owned by this ITokenMinter.\"},\"getLocalToken(uint32,bytes32)\":{\"notice\":\"Get the local token associated with the given remote domain and token.\"},\"mint(uint32,bytes32,address,uint256)\":{\"notice\":\"Mints `amount` of local tokens corresponding to the given (`sourceDomain`, `burnToken`) pair, to `to` address.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"ITokenMinter\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{"burn(address,uint256)":"9dc29fac","burnLimitsPerMessage(address)":"a56ec632","getLocalToken(uint32,bytes32)":"78a0565e","mint(uint32,bytes32,address,uint256)":"d54de06f"}},"solidity/SynapseCCTPV1_flat.sol:MessageTransmitter":{"code":"0x608060405234801561001057600080fd5b50610249806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c80638371744e116100505780638371744e146100a75780638d3638f4146100c3578063f7259a75146100d257600080fd5b80632c1219211461006c57806357ecfd281461007f575b600080fd5b6040513081526020015b60405180910390f35b61009761008d366004610134565b6001949350505050565b6040519015158152602001610076565b60015b60405167ffffffffffffffff9091168152602001610076565b60405160018152602001610076565b6100aa6100e03660046101a0565b600195945050505050565b60008083601f8401126100fd57600080fd5b50813567ffffffffffffffff81111561011557600080fd5b60208301915083602082850101111561012d57600080fd5b9250929050565b6000806000806040858703121561014a57600080fd5b843567ffffffffffffffff8082111561016257600080fd5b61016e888389016100eb565b9096509450602087013591508082111561018757600080fd5b50610194878288016100eb565b95989497509550505050565b6000806000806000608086880312156101b857600080fd5b853563ffffffff811681146101cc57600080fd5b94506020860135935060408601359250606086013567ffffffffffffffff8111156101f657600080fd5b610202888289016100eb565b96999598509396509294939250505056fea26469706673582212202d056d4f7e5d33a986c7ac7e4529e3e6fe06c21d2cc169af4d552ca7e81f6b0764736f6c634300080d0033","runtime-code":"0x608060405234801561001057600080fd5b50600436106100675760003560e01c80638371744e116100505780638371744e146100a75780638d3638f4146100c3578063f7259a75146100d257600080fd5b80632c1219211461006c57806357ecfd281461007f575b600080fd5b6040513081526020015b60405180910390f35b61009761008d366004610134565b6001949350505050565b6040519015158152602001610076565b60015b60405167ffffffffffffffff9091168152602001610076565b60405160018152602001610076565b6100aa6100e03660046101a0565b600195945050505050565b60008083601f8401126100fd57600080fd5b50813567ffffffffffffffff81111561011557600080fd5b60208301915083602082850101111561012d57600080fd5b9250929050565b6000806000806040858703121561014a57600080fd5b843567ffffffffffffffff8082111561016257600080fd5b61016e888389016100eb565b9096509450602087013591508082111561018757600080fd5b50610194878288016100eb565b95989497509550505050565b6000806000806000608086880312156101b857600080fd5b853563ffffffff811681146101cc57600080fd5b94506020860135935060408601359250606086013567ffffffffffffffff8111156101f657600080fd5b610202888289016100eb565b96999598509396509294939250505056fea26469706673582212202d056d4f7e5d33a986c7ac7e4529e3e6fe06c21d2cc169af4d552ca7e81f6b0764736f6c634300080d0033","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"57508:674:0:-:0;;;;;;;;;;;;;;;;;;;","srcMapRuntime":"57508:674:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;58077:103;;;58168:4;160:74:1;;148:2;133:18;58077:103:0;;;;;;;;57542:133;;;;;;:::i;:::-;57664:4;57542:133;;;;;;;;;;1484:14:1;;1477:22;1459:41;;1447:2;1432:18;57542:133:0;1319:187:1;57987:85:0;58064:1;57987:85;;;1685:18:1;1673:31;;;1655:50;;1643:2;1628:18;57987:85:0;1511:200:1;57904:78:0;;;58064:1;1860:42:1;;1848:2;1833:18;57904:78:0;1716:192:1;57680:219:0;;;;;;:::i;:::-;57891:1;57680:219;;;;;;;;245:347:1;296:8;306:6;360:3;353:4;345:6;341:17;337:27;327:55;;378:1;375;368:12;327:55;-1:-1:-1;401:20:1;;444:18;433:30;;430:50;;;476:1;473;466:12;430:50;513:4;505:6;501:17;489:29;;565:3;558:4;549:6;541;537:19;533:30;530:39;527:59;;;582:1;579;572:12;527:59;245:347;;;;;:::o;597:717::-;687:6;695;703;711;764:2;752:9;743:7;739:23;735:32;732:52;;;780:1;777;770:12;732:52;820:9;807:23;849:18;890:2;882:6;879:14;876:34;;;906:1;903;896:12;876:34;945:58;995:7;986:6;975:9;971:22;945:58;:::i;:::-;1022:8;;-1:-1:-1;919:84:1;-1:-1:-1;1110:2:1;1095:18;;1082:32;;-1:-1:-1;1126:16:1;;;1123:36;;;1155:1;1152;1145:12;1123:36;;1194:60;1246:7;1235:8;1224:9;1220:24;1194:60;:::i;:::-;597:717;;;;-1:-1:-1;1273:8:1;-1:-1:-1;;;;597:717:1:o;1913:710::-;2009:6;2017;2025;2033;2041;2094:3;2082:9;2073:7;2069:23;2065:33;2062:53;;;2111:1;2108;2101:12;2062:53;2150:9;2137:23;2200:10;2193:5;2189:22;2182:5;2179:33;2169:61;;2226:1;2223;2216:12;2169:61;2249:5;-1:-1:-1;2301:2:1;2286:18;;2273:32;;-1:-1:-1;2352:2:1;2337:18;;2324:32;;-1:-1:-1;2407:2:1;2392:18;;2379:32;2434:18;2423:30;;2420:50;;;2466:1;2463;2456:12;2420:50;2505:58;2555:7;2546:6;2535:9;2531:22;2505:58;:::i;:::-;1913:710;;;;-1:-1:-1;1913:710:1;;-1:-1:-1;2582:8:1;;2479:84;1913:710;-1:-1:-1;;;1913:710:1:o","abiDefinition":[{"inputs":[],"name":"localDomain","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"localMessageTransmitter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextAvailableNonce","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"receiveMessage","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"internalType":"bytes32","name":"recipient","type":"bytes32"},{"internalType":"bytes32","name":"destinationCaller","type":"bytes32"},{"internalType":"bytes","name":"messageBody","type":"bytes"}],"name":"sendMessageWithCaller","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"nonpayable","type":"function"}],"userDoc":{"kind":"user","methods":{},"version":1},"developerDoc":{"kind":"dev","methods":{},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"localDomain\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"localMessageTransmitter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nextAvailableNonce\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"receiveMessage\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"destinationDomain\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"destinationCaller\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"messageBody\",\"type\":\"bytes\"}],\"name\":\"sendMessageWithCaller\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"MessageTransmitter\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{"localDomain()":"8d3638f4","localMessageTransmitter()":"2c121921","nextAvailableNonce()":"8371744e","receiveMessage(bytes,bytes)":"57ecfd28","sendMessageWithCaller(uint32,bytes32,bytes32,bytes)":"f7259a75"}},"solidity/SynapseCCTPV1_flat.sol:MinimalForwarderLib":{"code":"0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220d22902b1c05d6b3025fbd4b72fceca0666fbecd48c422cc220629906f9a9afb164736f6c634300080d0033","runtime-code":"0x73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220d22902b1c05d6b3025fbd4b72fceca0666fbecd48c422cc220629906f9a9afb164736f6c634300080d0033","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"81931:4120:0:-:0;;;;;;;;;;;;;;;-1:-1:-1;;;81931:4120:0;;;;;;;;;;;;;;;;;","srcMapRuntime":"81931:4120:0:-:0;;;;;;;;","abiDefinition":[],"userDoc":{"kind":"user","methods":{},"notice":"Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that forwards all calls to a any target address with any payload. Unlike EIP-1167, delegates calls are not used, so the forwarder contract is `msg.sender` as far as the target contract is concerned. # Minimal Forwarder Bytecode Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167). Following changes were made: - Target address is not saved in the deployed contract code, but is passed as a part of the payload. - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload. - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes. - The target address is derived using CALLDATALOAD. - CALLVALUE is used to pass the msg.value to the target contract. - `call()` is used instead of `delegatecall()`. ## Bytecode Table | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 | | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ | | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 | | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 | | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 | | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 | | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 | | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 | | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 | | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 | | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 | | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 | | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 | | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 | | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 | | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 | | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 | | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 | | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 | | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 | | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 | | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 | | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 | | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc | | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc | | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds | | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds | | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds | | 0x1d | 0xfd | 0xfd | revert | | | | | | | | | | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds | | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | | \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any). \u003e - Stack View (S7..S0) is shown after the execution of the opcode. \u003e - The stack elements are shown from top to bottom. \u003e Opcodes are typically dealing with the top stack elements, so they are shown first. \u003e - `cds` refers to the calldata size. \u003e - `rds` refers to the returndata size (which is zero before the first external call). \u003e - `val` refers to the provided `msg.value`. \u003e - `addr` refers to the target address loaded from calldata. \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left. \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success. ## Bytecode Explanation - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address). \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack. - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\". \u003e - `dup1` duplicates the top stack item. - `0x05..0x08` - Copy the target call payload to memory. \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements: \u003e memory offset to write to, calldata offset to read from, and length of the data to copy. - `0x09..0x11` - Prepare the stack for the `call` opcode. \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero \u003e after we perform the first external call. \u003e - `swap3` swaps the top stack item with the fourth stack item. \u003e - `callvalue` pushes `msg.value` onto the stack. \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack. \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address. \u003e - `gas` pushes the remaining gas onto the stack. - `0x12..0x12` - Call the target contract. \u003e - `call` issues an external call to a target address. \u003e - Pops seven top stack items: gas, target address, value, input offset, input length, \u003e memory offset to write return data to, and length of return data to write to memory. \u003e - Pushes on stack: 0 on failure, 1 on success. - `0x13..0x16` - Copy the return data to memory. \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack. \u003e - `dup3` duplicates the third stack item. \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements: \u003e memory offset to write to, return data offset to read from, and length of the data to copy. - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size. \u003e - `swap1` swaps the top stack item with the second stack item. \u003e - `swap2` swaps the top stack item with the third stack item. \u003e - `0x1e` refers to the position of the `jumpdest` opcode. \u003e It is used to jump to the `return` opcode, if call was successful. - `0x1c..0x1c` - Jump to 0x1e position, if call was successful. \u003e - `jumpi` pops two top stack items: jump destination and jump condition. \u003e If jump condition is nonzero, jumps to the jump destination. - `0x1d..0x1d` - Revert if call was unsuccessful. \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message. \u003e - This allows us to bubble the revert message from the external call. - `0x1e..0x1e` - Jump destination for successful call. \u003e - `jumpdest` is a no-op that marks a valid jump destination. - `0x1f..0x1f` - Return if call was successful. \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data. \u003e - This allows us to reuse the return data from the external call. # Minimal Forwarder Init Code Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol). Following changes were made: - Adjusted bytecode length to 32 bytes. - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack. \u003e `bytecode` refers to the bytecode specified in the above table. ## Init Code Table | Pos | OP | OP + Args | Description | S1 | S0 | | ---- | ---- | --------- | --------------- | --- | -------- | | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode | | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode | | 0x1c | 0x52 | 0x52 | mstore | | | | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 | | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 | | 0x20 | 0xf3 | 0xf3 | return | | | \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code. \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode. ## Init Code Explanation - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack. \u003e - `push32` pushes 32 bytes as a single stack item onto the stack. - `0x1b..0x1b` - Push 0 onto the stack. \u003e No external calls were made, so the return data size is 0. - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory. \u003e - `mstore` pops two top stack items: memory offset to write to and value to write. \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory. - `0x1d..0x1f` - Prepare stack for `return` opcode. \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory. - `0x20..0x20` - Return the Minimal Forwarder bytecode. \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data. \u003e - This allows us to return the Minimal Forwarder bytecode.","version":1},"developerDoc":{"kind":"dev","methods":{},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"notice\":\"Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that forwards all calls to a any target address with any payload. Unlike EIP-1167, delegates calls are not used, so the forwarder contract is `msg.sender` as far as the target contract is concerned. # Minimal Forwarder Bytecode Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167). Following changes were made: - Target address is not saved in the deployed contract code, but is passed as a part of the payload. - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload. - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes. - The target address is derived using CALLDATALOAD. - CALLVALUE is used to pass the msg.value to the target contract. - `call()` is used instead of `delegatecall()`. ## Bytecode Table | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 | | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ | | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 | | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 | | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 | | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 | | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 | | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 | | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 | | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 | | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 | | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 | | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 | | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 | | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 | | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 | | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 | | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 | | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 | | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 | | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 | | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 | | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 | | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc | | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc | | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds | | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds | | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds | | 0x1d | 0xfd | 0xfd | revert | | | | | | | | | | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds | | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | | \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any). \u003e - Stack View (S7..S0) is shown after the execution of the opcode. \u003e - The stack elements are shown from top to bottom. \u003e Opcodes are typically dealing with the top stack elements, so they are shown first. \u003e - `cds` refers to the calldata size. \u003e - `rds` refers to the returndata size (which is zero before the first external call). \u003e - `val` refers to the provided `msg.value`. \u003e - `addr` refers to the target address loaded from calldata. \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left. \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success. ## Bytecode Explanation - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address). \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack. - `0x04..0x04` - Duplicate the offset to use it later as \\\"payload length\\\". \u003e - `dup1` duplicates the top stack item. - `0x05..0x08` - Copy the target call payload to memory. \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements: \u003e memory offset to write to, calldata offset to read from, and length of the data to copy. - `0x09..0x11` - Prepare the stack for the `call` opcode. \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero \u003e after we perform the first external call. \u003e - `swap3` swaps the top stack item with the fourth stack item. \u003e - `callvalue` pushes `msg.value` onto the stack. \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack. \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address. \u003e - `gas` pushes the remaining gas onto the stack. - `0x12..0x12` - Call the target contract. \u003e - `call` issues an external call to a target address. \u003e - Pops seven top stack items: gas, target address, value, input offset, input length, \u003e memory offset to write return data to, and length of return data to write to memory. \u003e - Pushes on stack: 0 on failure, 1 on success. - `0x13..0x16` - Copy the return data to memory. \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack. \u003e - `dup3` duplicates the third stack item. \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements: \u003e memory offset to write to, return data offset to read from, and length of the data to copy. - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size. \u003e - `swap1` swaps the top stack item with the second stack item. \u003e - `swap2` swaps the top stack item with the third stack item. \u003e - `0x1e` refers to the position of the `jumpdest` opcode. \u003e It is used to jump to the `return` opcode, if call was successful. - `0x1c..0x1c` - Jump to 0x1e position, if call was successful. \u003e - `jumpi` pops two top stack items: jump destination and jump condition. \u003e If jump condition is nonzero, jumps to the jump destination. - `0x1d..0x1d` - Revert if call was unsuccessful. \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message. \u003e - This allows us to bubble the revert message from the external call. - `0x1e..0x1e` - Jump destination for successful call. \u003e - `jumpdest` is a no-op that marks a valid jump destination. - `0x1f..0x1f` - Return if call was successful. \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data. \u003e - This allows us to reuse the return data from the external call. # Minimal Forwarder Init Code Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol). Following changes were made: - Adjusted bytecode length to 32 bytes. - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack. \u003e `bytecode` refers to the bytecode specified in the above table. ## Init Code Table | Pos | OP | OP + Args | Description | S1 | S0 | | ---- | ---- | --------- | --------------- | --- | -------- | | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode | | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode | | 0x1c | 0x52 | 0x52 | mstore | | | | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 | | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 | | 0x20 | 0xf3 | 0xf3 | return | | | \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code. \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode. ## Init Code Explanation - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack. \u003e - `push32` pushes 32 bytes as a single stack item onto the stack. - `0x1b..0x1b` - Push 0 onto the stack. \u003e No external calls were made, so the return data size is 0. - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory. \u003e - `mstore` pops two top stack items: memory offset to write to and value to write. \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory. - `0x1d..0x1f` - Prepare stack for `return` opcode. \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory. - `0x20..0x20` - Return the Minimal Forwarder bytecode. \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data. \u003e - This allows us to return the Minimal Forwarder bytecode.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"MinimalForwarderLib\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{}},"solidity/SynapseCCTPV1_flat.sol:Ownable":{"code":"0x","runtime-code":"0x","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"","srcMapRuntime":"","abiDefinition":[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}],"userDoc":{"kind":"user","methods":{},"version":1},"developerDoc":{"details":"Contract module which provides a basic access control mechanism, where there is an account (an owner) that can be granted exclusive access to specific functions. By default, the owner account will be the one that deploys the contract. This can later be changed with {transferOwnership}. This module is used through inheritance. It will make available the modifier `onlyOwner`, which can be applied to your functions to restrict their use to the owner.","kind":"dev","methods":{"constructor":{"details":"Initializes the contract setting the deployer as the initial owner."},"owner()":{"details":"Returns the address of the current owner."},"renounceOwnership()":{"details":"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions anymore. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner."},"transferOwnership(address)":{"details":"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner."}},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"Contract module which provides a basic access control mechanism, where there is an account (an owner) that can be granted exclusive access to specific functions. By default, the owner account will be the one that deploys the contract. This can later be changed with {transferOwnership}. This module is used through inheritance. It will make available the modifier `onlyOwner`, which can be applied to your functions to restrict their use to the owner.\",\"kind\":\"dev\",\"methods\":{\"constructor\":{\"details\":\"Initializes the contract setting the deployer as the initial owner.\"},\"owner()\":{\"details\":\"Returns the address of the current owner.\"},\"renounceOwnership()\":{\"details\":\"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions anymore. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner.\"},\"transferOwnership(address)\":{\"details\":\"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"Ownable\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{"owner()":"8da5cb5b","renounceOwnership()":"715018a6","transferOwnership(address)":"f2fde38b"}},"solidity/SynapseCCTPV1_flat.sol:Pausable":{"code":"0x","runtime-code":"0x","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"","srcMapRuntime":"","abiDefinition":[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}],"userDoc":{"kind":"user","methods":{},"version":1},"developerDoc":{"details":"Contract module which allows children to implement an emergency stop mechanism that can be triggered by an authorized account. This module is used through inheritance. It will make available the modifiers `whenNotPaused` and `whenPaused`, which can be applied to the functions of your contract. Note that they will not be pausable by simply including this module, only once the modifiers are put in place.","events":{"Paused(address)":{"details":"Emitted when the pause is triggered by `account`."},"Unpaused(address)":{"details":"Emitted when the pause is lifted by `account`."}},"kind":"dev","methods":{"constructor":{"details":"Initializes the contract in unpaused state."},"paused()":{"details":"Returns true if the contract is paused, and false otherwise."}},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"Contract module which allows children to implement an emergency stop mechanism that can be triggered by an authorized account. This module is used through inheritance. It will make available the modifiers `whenNotPaused` and `whenPaused`, which can be applied to the functions of your contract. Note that they will not be pausable by simply including this module, only once the modifiers are put in place.\",\"events\":{\"Paused(address)\":{\"details\":\"Emitted when the pause is triggered by `account`.\"},\"Unpaused(address)\":{\"details\":\"Emitted when the pause is lifted by `account`.\"}},\"kind\":\"dev\",\"methods\":{\"constructor\":{\"details\":\"Initializes the contract in unpaused state.\"},\"paused()\":{\"details\":\"Returns true if the contract is paused, and false otherwise.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"Pausable\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{"paused()":"5c975abb"}},"solidity/SynapseCCTPV1_flat.sol:RequestLib":{"code":"0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea26469706673582212209285cee3e89e6a53b82254d8bc43097a5aa351ef18d8b329cb5ad4f03b72c79d64736f6c634300080d0033","runtime-code":"0x73000000000000000000000000000000000000000030146080604052600080fdfea26469706673582212209285cee3e89e6a53b82254d8bc43097a5aa351ef18d8b329cb5ad4f03b72c79d64736f6c634300080d0033","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"64324:7310:0:-:0;;;;;;;;;;;;;;;-1:-1:-1;;;64324:7310:0;;;;;;;;;;;;;;;;;","srcMapRuntime":"64324:7310:0:-:0;;;;;;;;","abiDefinition":[],"userDoc":{"kind":"user","methods":{},"notice":"# Base Request layout | Field | Type | Description | | --------------- | ------- | ---------------------------------------------- | | originDomain | uint32 | Domain of the origin chain used by Circle CCTP | | nonce | uint64 | Nonce of the CCTP message on origin chain | | originBurnToken | address | Circle token that was burned on origin chain | | amount | uint256 | Amount of tokens burned on origin chain | | recipient | address | Recipient of the tokens on destination chain | # Swap Params layout | Field | Type | Description | | -------------- | ------- | ------------------------------------------------------------- | | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool | | tokenIndexTo | uint8 | Index of the final token in the pool | | deadline | uint256 | Latest timestamp to execute the swap | | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |","version":1},"developerDoc":{"kind":"dev","methods":{},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"notice\":\"# Base Request layout | Field | Type | Description | | --------------- | ------- | ---------------------------------------------- | | originDomain | uint32 | Domain of the origin chain used by Circle CCTP | | nonce | uint64 | Nonce of the CCTP message on origin chain | | originBurnToken | address | Circle token that was burned on origin chain | | amount | uint256 | Amount of tokens burned on origin chain | | recipient | address | Recipient of the tokens on destination chain | # Swap Params layout | Field | Type | Description | | -------------- | ------- | ------------------------------------------------------------- | | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool | | tokenIndexTo | uint8 | Index of the final token in the pool | | deadline | uint256 | Latest timestamp to execute the swap | | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"RequestLib\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{}},"solidity/SynapseCCTPV1_flat.sol:SafeERC20":{"code":"0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea264697066735822122033edcb017b82f4414ecbfed038a6cf6f212fb5854cfdc0f06f5dc5353f49ca6a64736f6c634300080d0033","runtime-code":"0x73000000000000000000000000000000000000000030146080604052600080fdfea264697066735822122033edcb017b82f4414ecbfed038a6cf6f212fb5854cfdc0f06f5dc5353f49ca6a64736f6c634300080d0033","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"89302:3270:0:-:0;;;;;;;;;;;;;;;-1:-1:-1;;;89302:3270:0;;;;;;;;;;;;;;;;;","srcMapRuntime":"89302:3270:0:-:0;;;;;;;;","abiDefinition":[],"userDoc":{"kind":"user","methods":{},"version":1},"developerDoc":{"details":"Wrappers around ERC20 operations that throw on failure (when the token contract returns false). Tokens that return no value (and instead revert or throw on failure) are also supported, non-reverting calls are assumed to be successful. To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, which allows you to call the safe operations as `token.safeTransfer(...)`, etc.","kind":"dev","methods":{},"title":"SafeERC20","version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"details\":\"Wrappers around ERC20 operations that throw on failure (when the token contract returns false). Tokens that return no value (and instead revert or throw on failure) are also supported, non-reverting calls are assumed to be successful. To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\",\"kind\":\"dev\",\"methods\":{},\"title\":\"SafeERC20\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"SafeERC20\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{}},"solidity/SynapseCCTPV1_flat.sol:SwapQueryLib":{"code":"0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220bbbeda60024ccff66bcc1e0ae109d1a5915d4973caffea3532870c1c68bd97f564736f6c634300080d0033","runtime-code":"0x73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220bbbeda60024ccff66bcc1e0ae109d1a5915d4973caffea3532870c1c68bd97f564736f6c634300080d0033","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"3380:1048:0:-:0;;;;;;;;;;;;;;;-1:-1:-1;;;3380:1048:0;;;;;;;;;;;;;;;;;","srcMapRuntime":"3380:1048:0:-:0;;;;;;;;","abiDefinition":[],"userDoc":{"kind":"user","methods":{},"version":1},"developerDoc":{"kind":"dev","methods":{},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"SwapQueryLib\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{}},"solidity/SynapseCCTPV1_flat.sol:SynapseCCTP":{"code":"0x60e06040523480156200001157600080fd5b50604051620048a5380380620048a58339810160408190526200003491620001af565b6200003f3362000146565b600a805460ff191690556001600160a01b03821660c081905260408051632c12192160e01b81529051632c121921916004808201926020929091908290030181865afa15801562000094573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000ba9190620001ee565b6001600160a01b031660a08190526040805163234d8e3d60e21b81529051638d3638f4916004808201926020929091908290030181865afa15801562000104573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200012a919062000215565b63ffffffff166080526200013e8162000146565b50506200023d565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b0381168114620001ac57600080fd5b50565b60008060408385031215620001c357600080fd5b8251620001d08162000196565b6020840151909250620001e38162000196565b809150509250929050565b6000602082840312156200020157600080fd5b81516200020e8162000196565b9392505050565b6000602082840312156200022857600080fd5b815163ffffffff811681146200020e57600080fd5b60805160a05160c051614600620002a56000396000818161037401528181610d6201528181610d9101526124f801526000818161047401528181610bb301526124780152600081816104a801528181610c3e015281816110110152611a8a01526146006000f3fe6080604052600436106102345760003560e01c80638da5cb5b11610138578063d77938e4116100b0578063e9259ab91161007f578063f2fde38b11610064578063f2fde38b146107c5578063f7265b3a146107e5578063f879a41a1461080557600080fd5b8063e9259ab91461073b578063e9bbb36d146107a557600080fd5b8063d77938e414610644578063dc72495b14610659578063e00a83e014610710578063e7a64a801461072657600080fd5b8063a4b1d03411610107578063b0e21e8a116100ec578063b0e21e8a146105d6578063b250fe6b146105ec578063d4a67c6d1461060c57600080fd5b8063a4b1d0341461055f578063a5bc29c21461059557600080fd5b80638da5cb5b146104df57806392a442ea146104fd5780639c1d060e1461051d578063a42dce801461053f57600080fd5b80634a5ae51d116101cb5780635fa7b5841161019a578063787dce3d1161017f578063787dce3d146104425780637b04c181146104625780638d3638f41461049657600080fd5b80635fa7b5841461040d578063715018a61461042d57600080fd5b80634a5ae51d146103965780634a85178d146103a95780634bdb4eed146103c95780635c975abb146103e957600080fd5b8063304ddb4c11610207578063304ddb4c146102df57806340432d51146102ff57806341f355ee14610314578063461178301461036257600080fd5b80630ba36121146102395780630d25aafe1461026f5780632cc9e7e51461029d5780632d80caa5146102bf575b600080fd5b34801561024557600080fd5b50610259610254366004613b34565b610825565b6040516102669190613ba9565b60405180910390f35b34801561027b57600080fd5b5061028f61028a366004613bca565b6108bf565b604051908152602001610266565b3480156102a957600080fd5b506102bd6102b8366004613c0c565b6108d6565b005b3480156102cb57600080fd5b506102bd6102da366004613b34565b6109fc565b3480156102eb57600080fd5b506102bd6102fa366004613d35565b610b0f565b34801561030b57600080fd5b506102bd610ec3565b34801561032057600080fd5b5061034a61032f366004613b34565b6005602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610266565b34801561036e57600080fd5b5061034a7f000000000000000000000000000000000000000000000000000000000000000081565b6102bd6103a4366004613dfe565b610fa2565b3480156103b557600080fd5b506102bd6103c4366004613e98565b61112e565b3480156103d557600080fd5b506102bd6103e4366004613f0a565b6112a9565b3480156103f557600080fd5b50600a5460ff165b6040519015158152602001610266565b34801561041957600080fd5b506102bd610428366004613b34565b611358565b34801561043957600080fd5b506102bd611520565b34801561044e57600080fd5b506102bd61045d366004613f4e565b611586565b34801561046e57600080fd5b5061034a7f000000000000000000000000000000000000000000000000000000000000000081565b3480156104a257600080fd5b506104ca7f000000000000000000000000000000000000000000000000000000000000000081565b60405163ffffffff9091168152602001610266565b3480156104eb57600080fd5b506000546001600160a01b031661034a565b34801561050957600080fd5b506103fd610518366004613f4e565b611665565b34801561052957600080fd5b50610532611683565b6040516102669190613f67565b34801561054b57600080fd5b506102bd61055a366004613b34565b611810565b34801561056b57600080fd5b5061034a61057a366004613b34565b600c602052600090815260409020546001600160a01b031681565b3480156105a157600080fd5b5061034a6105b0366004614004565b80516020818301810180516002825292820191909301209152546001600160a01b031681565b3480156105e257600080fd5b5061028f60065481565b3480156105f857600080fd5b506102bd610607366004613f4e565b611898565b34801561061857600080fd5b5061028f610627366004613c0c565b600460209081526000928352604080842090915290825290205481565b34801561065057600080fd5b506102bd611927565b34801561066557600080fd5b506106d7610674366004613b34565b60036020526000908152604090205464ffffffffff81169068ffffffffffffffffff6501000000000082048116916e0100000000000000000000000000008104821691770100000000000000000000000000000000000000000000009091041684565b6040805164ffffffffff909516855268ffffffffffffffffff938416602086015291831691840191909152166060820152608001610266565b34801561071c57600080fd5b5061028f60075481565b34801561073257600080fd5b506102bd611989565b34801561074757600080fd5b50610781610756366004613f4e565b600b6020526000908152604090205463ffffffff81169064010000000090046001600160a01b031682565b6040805163ffffffff90931683526001600160a01b03909116602083015201610266565b3480156107b157600080fd5b506102bd6107c0366004614039565b6119eb565b3480156107d157600080fd5b506102bd6107e0366004613b34565b611be1565b3480156107f157600080fd5b506102bd610800366004613b34565b611cc0565b34801561081157600080fd5b5061034a610820366004614070565b611d4f565b6001602052600090815260409020805461083e9061408e565b80601f016020809104026020016040519081016040528092919081815260200182805461086a9061408e565b80156108b75780601f1061088c576101008083540402835291602001916108b7565b820191906000526020600020905b81548152906001019060200180831161089a57829003601f168201915b505050505081565b60006108cc848484611d64565b90505b9392505050565b6000546001600160a01b031633146109355760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b6001600160a01b038216610975576040517f24305eca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610980600883611e6e565b6109b6576040517f53b5a66c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b039182166000908152600c6020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001691909216179055565b6000546001600160a01b03163314610a565760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b6001600160a01b03811660009081527f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ec602052604081205490819003610ac8576040517f30b93f1d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03821660008181527f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ec6020526040812055610b0b903383611e90565b5050565b600a5460ff1615610b625760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a2070617573656400000000000000000000000000000000604482015260640161092c565b610b6d600885611e6e565b610ba3576040517f53b5a66c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610bad8484611f3e565b925060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638371744e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c3391906140f8565b6040805163ffffffff7f000000000000000000000000000000000000000000000000000000000000000016602082015267ffffffffffffffff8316818301526001600160a01b038089166060830152608082018890528a1660a0808301919091528251808303909101815260c0909101909152909150600090610cb890859085612070565b6000888152600b6020908152604080832081518083019092525463ffffffff8116825264010000000090046001600160a01b031691810182905292935090819003610d2f576040517fa86a3b0e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8151835160208086019190912067ffffffff0000000083831b1663ffffffff8a16176000908152915260409020610d878a7f00000000000000000000000000000000000000000000000000000000000000008b61219d565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663f856ddb68a84868e610dc4828861226b565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b168152600481019590955263ffffffff93909316602485015260448401919091526001600160a01b03166064830152608482015260a4016020604051808303816000875af1158015610e43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6791906140f8565b50326001600160a01b03167fc4980459837e213aedb84d9046eab1db050fec66cb9e046c4fe3b5578b01b20c8c888d8d8d8b88604051610ead9796959493929190614113565b60405180910390a2505050505050505050505050565b6000546001600160a01b03163314610f1d5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b604051600090339047908381818185875af1925050503d8060008114610f5f576040519150601f19603f3d011682016040523d82523d6000602084013e610f64565b606091505b5050905080610f9f576040517f4e5610fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b6007543414610fdd576040517fc561806500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080610fea848461228f565b91509150600080600080610ffd8661236f565b8b516020808e019190912063ffffffff8f167f0000000000000000000000000000000000000000000000000000000000000000831b67ffffffff0000000016176000908152915260408120959950929750909550935091905090506110658d8d8d8d856123d8565b600061107186866124f3565b90506000611089828663ffffffff8e1660011461266f565b909550905060008061109d8685898d6128bf565b909250905034156110b1576110b1866129d8565b6040805163ffffffff8b1681526001600160a01b03868116602083015291810185905283821660608201526080810183905260a08101879052908716907f7864397c00beabf21ab17a04795e450354505d879a634dd2632f4fdc4b5ba04e9060c00160405180910390a25050505050505050505050505050505050565b6000546001600160a01b031633146111885760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b6001600160a01b0385166111c8576040517f76998feb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111d3600886612a72565b611209576040517f1191732500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61121286612a87565b6001600160a01b0385166000908152600160209081526040909120875161123b92890190613a54565b508460028760405161124d9190614171565b90815260405190819003602001902080546001600160a01b03929092167fffffffffffffffffffffffff00000000000000000000000000000000000000009092169190911790556112a18585858585612c27565b505050505050565b6000546001600160a01b031633146113035760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b61130e600886611e6e565b611344576040517f53b5a66c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113518585858585612c27565b5050505050565b6000546001600160a01b031633146113b25760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b6113bd600882612e1d565b6113f3576040517f53b5a66c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116600090815260016020526040812080546114169061408e565b80601f01602080910402602001604051908101604052809291908181526020018280546114429061408e565b801561148f5780601f106114645761010080835404028352916020019161148f565b820191906000526020600020905b81548152906001019060200180831161147257829003601f168201915b505050506001600160a01b03841660009081526001602052604081209293506114b9929150613ad4565b6002816040516114c99190614171565b908152604080516020928190038301902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556001600160a01b0393909316600090815260039091529182209190915550565b6000546001600160a01b0316331461157a5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b6115846000612e32565b565b6000546001600160a01b031633146115e05760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b6115f060026402540be4006141bc565b811115611629576040517f28562c4700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60068190556040518181527fd10d75876659a287a59a6ccfa2e3fff42f84d94b542837acd30bc184d562de40906020015b60405180910390a150565b6000806116723084612e9a565b6001600160a01b03163b1192915050565b606060006116916008612f86565b90508067ffffffffffffffff8111156116ac576116ac613c57565b6040519080825280602002602001820160405280156116f257816020015b6040805180820190915260608152600060208201528152602001906001900390816116ca5790505b50915060005b8181101561180b57600061170d600883612f90565b9050604051806040016040528060016000846001600160a01b03166001600160a01b03168152602001908152602001600020805461174a9061408e565b80601f01602080910402602001604051908101604052809291908181526020018280546117769061408e565b80156117c35780601f10611798576101008083540402835291602001916117c3565b820191906000526020600020905b8154815290600101906020018083116117a657829003601f168201915b50505050508152602001826001600160a01b03168152508483815181106117ec576117ec6141f7565b602002602001018190525050808061180390614226565b9150506116f8565b505090565b3360008181526005602090815260409182902080547fffffffffffffffffffffffff000000000000000000000000000000000000000081166001600160a01b03878116918217909355845192909116808352928201529092917f9dfcadd14a1ddfb19c51e84b87452ca32a43c5559e9750d1575c77105cdeac1e910160405180910390a25050565b6000546001600160a01b031633146118f25760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b60078190556040518181527f5e8bad84cb22c143a6757c7f1252a7d53493816880330977cc99bb7c15aaf6b49060200161165a565b6000546001600160a01b031633146119815760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b611584612f9c565b6000546001600160a01b031633146119e35760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b61158461305f565b6000546001600160a01b03163314611a455760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b821580611a5157504683145b15611a88576040517f3f8f40a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000063ffffffff168263ffffffff1603611aed576040517f93c970c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8216156001841414611b30576040517f93c970c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116611b70576040517f24305eca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051808201825263ffffffff93841681526001600160a01b0392831660208083019182526000968752600b905291909420935184549151909216640100000000027fffffffffffffffff0000000000000000000000000000000000000000000000009091169190921617179055565b6000546001600160a01b03163314611c3b5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b6001600160a01b038116611cb75760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161092c565b610f9f81612e32565b3360009081526004602090815260408083206001600160a01b038516845290915281205490819003611d1e576040517f30b93f1d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360008181526004602090815260408083206001600160a01b0387168085529252822091909155610b0b9183611e90565b6000611d5b83836124f3565b90505b92915050565b6001600160a01b03831660009081526003602090815260408083208151608081018352905464ffffffffff811680835268ffffffffffffffffff6501000000000083048116958401959095526e010000000000000000000000000000820485169383019390935277010000000000000000000000000000000000000000000000900490921660608301526402540be40090611dff9086614240565b611e0991906141bc565b9150600083611e1c578160200151611e22565b81604001515b68ffffffffffffffffff16905080831015611e3b578092505b816060015168ffffffffffffffffff16831115611e6557816060015168ffffffffffffffffff1692505b50509392505050565b6001600160a01b03811660009081526001830160205260408120541515611d5b565b6040516001600160a01b038316602482015260448101829052611f399084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613100565b505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009081906001600160a01b038516906370a0823190602401602060405180830381865afa158015611fa0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fc4919061425f565b9050611fdb6001600160a01b0385163330866131e5565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015281906001600160a01b038616906370a0823190602401602060405180830381865afa15801561203a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061205e919061425f565b6120689190614278565b949350505050565b606060a08351146120ad576040517f74593f8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff84166120f8578151156120f1576040517f74593f8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50816108cf565b60001963ffffffff85160161216b576080825114612142576040517f74593f8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b828260405160200161215592919061428f565b60405160208183030381529060405290506108cf565b6040517f523fa8d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e90604401602060405180830381865afa158015612206573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061222a919061425f565b90508181101561226557801561224f5761224f6001600160a01b038516846000613236565b6122656001600160a01b03851684600019613236565b50505050565b6000611d5b6122836001600160a01b03851684612e9a565b6001600160a01b031690565b60608063ffffffff84166122f05760a08351146122d8576040517f74593f8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50506040805160208101909152600081528190612368565b60001963ffffffff85160161216b57608061230c60a0826142b4565b61231691906142b4565b83511461234f576040517f74593f8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b828060200190518101906123639190614311565b915091505b9250929050565b600080600080600060a08651146123b2576040517f74593f8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b858060200190518101906123c69190614375565b939a9299509097509550909350915050565b60006123e382613384565b905060006357ecfd2860e01b878787876040516024016124069493929190614407565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529050600061249d6001600160a01b0384167f000000000000000000000000000000000000000000000000000000000000000084613430565b9050808060200190518101906124b3919061442e565b6124e9576040517f182f34eb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050505050565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663cb75c11c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612554573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612578919061444b565b9050806001600160a01b03166378a0565e856125a3866001600160a01b03166001600160a01b031690565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815263ffffffff9290921660048301526024820152604401602060405180830381865afa158015612602573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612626919061444b565b91506001600160a01b038216612668576040517f53b5a66c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5092915050565b60008061267d600886611e6e565b6126b3576040517f53b5a66c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6126be858585611d64565b90508381106126f9576040517f3eae42e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360009081526005602052604090205481850392506001600160a01b0316806127ae576001600160a01b03861660009081527f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ec6020526040812080548492906127639084906142b4565b909155505060408051600080825260208201529081018390527f108516ddcf5ba43cea6bb2cd5ff6d59ac196c1c86ccb9178332b9dd72d1ca5619060600160405180910390a16128b6565b60006402540be400600654846127c49190614240565b6127ce91906141bc565b905060006127dc8285614278565b6001600160a01b03891660009081527f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ec60205260408120805492935084929091906128289084906142b4565b90915550506001600160a01b038084166000908152600460209081526040808320938c16835292905290812080548392906128649084906142b4565b9091555050604080516001600160a01b0385168152602081018390529081018390527f108516ddcf5ba43cea6bb2cd5ff6d59ac196c1c86ccb9178332b9dd72d1ca5619060600160405180910390a150505b50935093915050565b60008082516000036128e9576128df6001600160a01b0386168786611e90565b50839050826129cf565b6001600160a01b038086166000908152600c602052604090205416806129295761291d6001600160a01b0387168887611e90565b858592509250506129cf565b6000806000806129388861343f565b935093509350935061294a85846134a3565b96506001600160a01b03871661297e5761296e6001600160a01b038b168c8b611e90565b89899650965050505050506129cf565b6129898a868b61219d565b6129978585858c86866135a6565b9550856000036129b55761296e6001600160a01b038b168c8b611e90565b6129c96001600160a01b0388168c88611e90565b50505050505b94509492505050565b6000816001600160a01b03163460405160006040518083038185875af1925050503d8060008114612a25576040519150601f19603f3d011682016040523d82523d6000602084013e612a2a565b606091505b505090507ff9b0951a3a6282341e1ba9414555d42d04e99076337702ee6dc484a706bfd68381612a5b576000612a5d565b345b60405190815260200160405180910390a15050565b6000611d5b836001600160a01b03841661365d565b60006001600160a01b0316600282604051612aa29190614171565b908152604051908190036020019020546001600160a01b031614612af2576040517f82ca3adf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80518190600510612b2f576040517f3f8fe5a800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b6005811015611f39576040518060400160405280600581526020017f434354502e0000000000000000000000000000000000000000000000000000008152508181518110612b8257612b826141f7565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916828281518110612bc157612bc16141f7565b01602001517fff000000000000000000000000000000000000000000000000000000000000001614612c1f576040517f3f8fe5a800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600101612b32565b62989680841115612c64576040517f76998feb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81831115612c9e576040517f76998feb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80821115612cd8576040517f76998feb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518060800160405280612cec866136ac565b64ffffffffff168152602001612d01856136f1565b68ffffffffffffffffff168152602001612d1a846136f1565b68ffffffffffffffffff168152602001612d33836136f1565b68ffffffffffffffffff9081169091526001600160a01b039096166000908152600360209081526040918290208351815492850151938501516060909501518a16770100000000000000000000000000000000000000000000000276ffffffffffffffffffffffffffffffffffffffffffffff958b166e01000000000000000000000000000002959095166dffffffffffffffffffffffffffff94909a1665010000000000027fffffffffffffffffffffffffffffffffffff000000000000000000000000000090931664ffffffffff909116179190911791909116969096171790945550505050565b6000611d5b836001600160a01b038416613736565b600080546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000611d5b83836040518060400160405280602081526020017f602036038060203d373d3d3d923d343d355af13d82803e903d91601e57fd5bf3815250604051602001612ee79190614468565b60405160208183030381529060405280519060200120604051602001612f6d939291907fff00000000000000000000000000000000000000000000000000000000000000815260609390931b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660018401526015830191909152603582015260550190565b6040516020818303038152906040528051906020012090565b6000611d5e825490565b6000611d5b8383613829565b600a5460ff1615612fef5760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a2070617573656400000000000000000000000000000000604482015260640161092c565b600a80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586130423390565b6040516001600160a01b03909116815260200160405180910390a1565b600a5460ff166130b15760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f7420706175736564000000000000000000000000604482015260640161092c565b600a80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33613042565b6000613155826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166138539092919063ffffffff16565b805190915015611f395780806020019051810190613173919061442e565b611f395760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161092c565b6040516001600160a01b03808516602483015283166044820152606481018290526122659085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401611ed5565b8015806132c957506040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa1580156132a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132c7919061425f565b155b61333b5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e636500000000000000000000606482015260840161092c565b6040516001600160a01b038316602482015260448101829052611f399084907f095ea7b30000000000000000000000000000000000000000000000000000000090606401611ed5565b6000806040518060400160405280602081526020017f602036038060203d373d3d3d923d343d355af13d82803e903d91601e57fd5bf38152506040516020016133cd9190614468565b6040516020818303038152906040529050828151602083016000f591506001600160a01b03821661342a576040517f27afa9fc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50919050565b60606108cc8484846000613862565b6000806000806080855114613480576040517f74593f8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8480602001905181019061349491906144e5565b93509350935093509193509193565b6040805160ff831660248083019190915282518083039091018152604490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f82b86600000000000000000000000000000000000000000000000000000000001790529051600091829182916001600160a01b0387169161352b9190614171565b600060405180830381855afa9150503d8060008114613566576040519150601f19603f3d011682016040523d82523d6000602084013e61356b565b606091505b509150915081801561357e575080516020145b156135995761359261358f82614528565b90565b925061359e565b600092505b505092915050565b6040517f9169558600000000000000000000000000000000000000000000000000000000815260ff8087166004830152851660248201526044810184905260648101829052608481018390526000906001600160a01b0388169063916955869060a4016020604051808303816000875af1925050508015613644575060408051601f3d908101601f191682019092526136419181019061425f565b60015b61365057506000613653565b90505b9695505050505050565b60008181526001830160205260408120546136a457508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155611d5e565b506000611d5e565b600064ffffffffff8211156136ed576040517fe58d471800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090565b600068ffffffffffffffffff8211156136ed576040517fe58d471800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818152600183016020526040812054801561381f57600061375a600183614278565b855490915060009061376e90600190614278565b90508181146137d357600086600001828154811061378e5761378e6141f7565b90600052602060002001549050808760000184815481106137b1576137b16141f7565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806137e4576137e461454c565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050611d5e565b6000915050611d5e565b6000826000018281548110613840576138406141f7565b9060005260206000200154905092915050565b60606108cc84846000856138ad565b60606138a46001600160a01b0385168460405160200161388392919061457b565b60408051601f198184030181529190526001600160a01b03871690846139f5565b95945050505050565b6060824710156139255760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c0000000000000000000000000000000000000000000000000000606482015260840161092c565b6001600160a01b0385163b61397c5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161092c565b600080866001600160a01b031685876040516139989190614171565b60006040518083038185875af1925050503d80600081146139d5576040519150601f19603f3d011682016040523d82523d6000602084013e6139da565b606091505b50915091506139ea828286613a1b565b979650505050505050565b60606108cc8484846040518060600160405280602981526020016145a2602991396138ad565b60608315613a2a5750816108cf565b825115613a3a5782518084602001fd5b8160405162461bcd60e51b815260040161092c9190613ba9565b828054613a609061408e565b90600052602060002090601f016020900481019282613a825760008555613ac8565b82601f10613a9b57805160ff1916838001178555613ac8565b82800160010185558215613ac8579182015b82811115613ac8578251825591602001919060010190613aad565b506136ed929150613b0a565b508054613ae09061408e565b6000825580601f10613af0575050565b601f016020900490600052602060002090810190610f9f91905b5b808211156136ed5760008155600101613b0b565b6001600160a01b0381168114610f9f57600080fd5b600060208284031215613b4657600080fd5b81356108cf81613b1f565b60005b83811015613b6c578181015183820152602001613b54565b838111156122655750506000910152565b60008151808452613b95816020860160208601613b51565b601f01601f19169290920160200192915050565b602081526000611d5b6020830184613b7d565b8015158114610f9f57600080fd5b600080600060608486031215613bdf57600080fd5b8335613bea81613b1f565b9250602084013591506040840135613c0181613bbc565b809150509250925092565b60008060408385031215613c1f57600080fd5b8235613c2a81613b1f565b91506020830135613c3a81613b1f565b809150509250929050565b63ffffffff81168114610f9f57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715613caf57613caf613c57565b604052919050565b600067ffffffffffffffff821115613cd157613cd1613c57565b50601f01601f191660200190565b600082601f830112613cf057600080fd5b8135613d03613cfe82613cb7565b613c86565b818152846020838601011115613d1857600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060c08789031215613d4e57600080fd5b8635613d5981613b1f565b9550602087013594506040870135613d7081613b1f565b9350606087013592506080870135613d8781613c45565b915060a087013567ffffffffffffffff811115613da357600080fd5b613daf89828a01613cdf565b9150509295509295509295565b60008083601f840112613dce57600080fd5b50813567ffffffffffffffff811115613de657600080fd5b60208301915083602082850101111561236857600080fd5b60008060008060008060808789031215613e1757600080fd5b863567ffffffffffffffff80821115613e2f57600080fd5b613e3b8a838b01613dbc565b90985096506020890135915080821115613e5457600080fd5b613e608a838b01613dbc565b909650945060408901359150613e7582613c45565b90925060608801359080821115613e8b57600080fd5b50613daf89828a01613cdf565b60008060008060008060c08789031215613eb157600080fd5b863567ffffffffffffffff811115613ec857600080fd5b613ed489828a01613cdf565b9650506020870135613ee581613b1f565b95989597505050506040840135936060810135936080820135935060a0909101359150565b600080600080600060a08688031215613f2257600080fd5b8535613f2d81613b1f565b97602087013597506040870135966060810135965060800135945092505050565b600060208284031215613f6057600080fd5b5035919050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b83811015613ff6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc089840301855281518051878552613fd088860182613b7d565b918901516001600160a01b03169489019490945294870194925090860190600101613f8e565b509098975050505050505050565b60006020828403121561401657600080fd5b813567ffffffffffffffff81111561402d57600080fd5b61206884828501613cdf565b60008060006060848603121561404e57600080fd5b83359250602084013561406081613c45565b91506040840135613c0181613b1f565b6000806040838503121561408357600080fd5b8235613c2a81613c45565b600181811c908216806140a257607f821691505b60208210810361342a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b805167ffffffffffffffff811681146140f357600080fd5b919050565b60006020828403121561410a57600080fd5b611d5b826140db565b87815267ffffffffffffffff871660208201526001600160a01b038616604082015284606082015263ffffffff8416608082015260e060a0820152600061415d60e0830185613b7d565b90508260c083015298975050505050505050565b60008251614183818460208701613b51565b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000826141f2577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060001982036142395761423961418d565b5060010190565b600081600019048311821515161561425a5761425a61418d565b500290565b60006020828403121561427157600080fd5b5051919050565b60008282101561428a5761428a61418d565b500390565b6040815260006142a26040830185613b7d565b82810360208401526138a48185613b7d565b600082198211156142c7576142c761418d565b500190565b600082601f8301126142dd57600080fd5b81516142eb613cfe82613cb7565b81815284602083860101111561430057600080fd5b612068826020830160208701613b51565b6000806040838503121561432457600080fd5b825167ffffffffffffffff8082111561433c57600080fd5b614348868387016142cc565b9350602085015191508082111561435e57600080fd5b5061436b858286016142cc565b9150509250929050565b600080600080600060a0868803121561438d57600080fd5b855161439881613c45565b94506143a6602087016140db565b935060408601516143b681613b1f565b6060870151608088015191945092506143ce81613b1f565b809150509295509295909350565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b60408152600061441b6040830186886143dc565b82810360208401526139ea8185876143dc565b60006020828403121561444057600080fd5b81516108cf81613bbc565b60006020828403121561445d57600080fd5b81516108cf81613b1f565b7f7f000000000000000000000000000000000000000000000000000000000000008152600082516144a0816001850160208701613b51565b7f3d5260203df300000000000000000000000000000000000000000000000000006001939091019283015250600701919050565b805160ff811681146140f357600080fd5b600080600080608085870312156144fb57600080fd5b614504856144d4565b9350614512602086016144d4565b6040860151606090960151949790965092505050565b8051602080830151919081101561342a5760001960209190910360031b1b16919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b82815260008251614593816020850160208701613b51565b91909101602001939250505056fe416464726573733a206c6f772d6c6576656c2063616c6c20776974682076616c7565206661696c6564a2646970667358221220599a9c446de9cb152ce66f97e0b3fbf4a2a0aebd13e35d04745bca99d587c69064736f6c634300080d0033","runtime-code":"0x6080604052600436106102345760003560e01c80638da5cb5b11610138578063d77938e4116100b0578063e9259ab91161007f578063f2fde38b11610064578063f2fde38b146107c5578063f7265b3a146107e5578063f879a41a1461080557600080fd5b8063e9259ab91461073b578063e9bbb36d146107a557600080fd5b8063d77938e414610644578063dc72495b14610659578063e00a83e014610710578063e7a64a801461072657600080fd5b8063a4b1d03411610107578063b0e21e8a116100ec578063b0e21e8a146105d6578063b250fe6b146105ec578063d4a67c6d1461060c57600080fd5b8063a4b1d0341461055f578063a5bc29c21461059557600080fd5b80638da5cb5b146104df57806392a442ea146104fd5780639c1d060e1461051d578063a42dce801461053f57600080fd5b80634a5ae51d116101cb5780635fa7b5841161019a578063787dce3d1161017f578063787dce3d146104425780637b04c181146104625780638d3638f41461049657600080fd5b80635fa7b5841461040d578063715018a61461042d57600080fd5b80634a5ae51d146103965780634a85178d146103a95780634bdb4eed146103c95780635c975abb146103e957600080fd5b8063304ddb4c11610207578063304ddb4c146102df57806340432d51146102ff57806341f355ee14610314578063461178301461036257600080fd5b80630ba36121146102395780630d25aafe1461026f5780632cc9e7e51461029d5780632d80caa5146102bf575b600080fd5b34801561024557600080fd5b50610259610254366004613b34565b610825565b6040516102669190613ba9565b60405180910390f35b34801561027b57600080fd5b5061028f61028a366004613bca565b6108bf565b604051908152602001610266565b3480156102a957600080fd5b506102bd6102b8366004613c0c565b6108d6565b005b3480156102cb57600080fd5b506102bd6102da366004613b34565b6109fc565b3480156102eb57600080fd5b506102bd6102fa366004613d35565b610b0f565b34801561030b57600080fd5b506102bd610ec3565b34801561032057600080fd5b5061034a61032f366004613b34565b6005602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610266565b34801561036e57600080fd5b5061034a7f000000000000000000000000000000000000000000000000000000000000000081565b6102bd6103a4366004613dfe565b610fa2565b3480156103b557600080fd5b506102bd6103c4366004613e98565b61112e565b3480156103d557600080fd5b506102bd6103e4366004613f0a565b6112a9565b3480156103f557600080fd5b50600a5460ff165b6040519015158152602001610266565b34801561041957600080fd5b506102bd610428366004613b34565b611358565b34801561043957600080fd5b506102bd611520565b34801561044e57600080fd5b506102bd61045d366004613f4e565b611586565b34801561046e57600080fd5b5061034a7f000000000000000000000000000000000000000000000000000000000000000081565b3480156104a257600080fd5b506104ca7f000000000000000000000000000000000000000000000000000000000000000081565b60405163ffffffff9091168152602001610266565b3480156104eb57600080fd5b506000546001600160a01b031661034a565b34801561050957600080fd5b506103fd610518366004613f4e565b611665565b34801561052957600080fd5b50610532611683565b6040516102669190613f67565b34801561054b57600080fd5b506102bd61055a366004613b34565b611810565b34801561056b57600080fd5b5061034a61057a366004613b34565b600c602052600090815260409020546001600160a01b031681565b3480156105a157600080fd5b5061034a6105b0366004614004565b80516020818301810180516002825292820191909301209152546001600160a01b031681565b3480156105e257600080fd5b5061028f60065481565b3480156105f857600080fd5b506102bd610607366004613f4e565b611898565b34801561061857600080fd5b5061028f610627366004613c0c565b600460209081526000928352604080842090915290825290205481565b34801561065057600080fd5b506102bd611927565b34801561066557600080fd5b506106d7610674366004613b34565b60036020526000908152604090205464ffffffffff81169068ffffffffffffffffff6501000000000082048116916e0100000000000000000000000000008104821691770100000000000000000000000000000000000000000000009091041684565b6040805164ffffffffff909516855268ffffffffffffffffff938416602086015291831691840191909152166060820152608001610266565b34801561071c57600080fd5b5061028f60075481565b34801561073257600080fd5b506102bd611989565b34801561074757600080fd5b50610781610756366004613f4e565b600b6020526000908152604090205463ffffffff81169064010000000090046001600160a01b031682565b6040805163ffffffff90931683526001600160a01b03909116602083015201610266565b3480156107b157600080fd5b506102bd6107c0366004614039565b6119eb565b3480156107d157600080fd5b506102bd6107e0366004613b34565b611be1565b3480156107f157600080fd5b506102bd610800366004613b34565b611cc0565b34801561081157600080fd5b5061034a610820366004614070565b611d4f565b6001602052600090815260409020805461083e9061408e565b80601f016020809104026020016040519081016040528092919081815260200182805461086a9061408e565b80156108b75780601f1061088c576101008083540402835291602001916108b7565b820191906000526020600020905b81548152906001019060200180831161089a57829003601f168201915b505050505081565b60006108cc848484611d64565b90505b9392505050565b6000546001600160a01b031633146109355760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b6001600160a01b038216610975576040517f24305eca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610980600883611e6e565b6109b6576040517f53b5a66c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b039182166000908152600c6020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001691909216179055565b6000546001600160a01b03163314610a565760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b6001600160a01b03811660009081527f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ec602052604081205490819003610ac8576040517f30b93f1d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03821660008181527f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ec6020526040812055610b0b903383611e90565b5050565b600a5460ff1615610b625760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a2070617573656400000000000000000000000000000000604482015260640161092c565b610b6d600885611e6e565b610ba3576040517f53b5a66c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610bad8484611f3e565b925060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638371744e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c3391906140f8565b6040805163ffffffff7f000000000000000000000000000000000000000000000000000000000000000016602082015267ffffffffffffffff8316818301526001600160a01b038089166060830152608082018890528a1660a0808301919091528251808303909101815260c0909101909152909150600090610cb890859085612070565b6000888152600b6020908152604080832081518083019092525463ffffffff8116825264010000000090046001600160a01b031691810182905292935090819003610d2f576040517fa86a3b0e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8151835160208086019190912067ffffffff0000000083831b1663ffffffff8a16176000908152915260409020610d878a7f00000000000000000000000000000000000000000000000000000000000000008b61219d565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663f856ddb68a84868e610dc4828861226b565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b168152600481019590955263ffffffff93909316602485015260448401919091526001600160a01b03166064830152608482015260a4016020604051808303816000875af1158015610e43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6791906140f8565b50326001600160a01b03167fc4980459837e213aedb84d9046eab1db050fec66cb9e046c4fe3b5578b01b20c8c888d8d8d8b88604051610ead9796959493929190614113565b60405180910390a2505050505050505050505050565b6000546001600160a01b03163314610f1d5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b604051600090339047908381818185875af1925050503d8060008114610f5f576040519150601f19603f3d011682016040523d82523d6000602084013e610f64565b606091505b5050905080610f9f576040517f4e5610fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b6007543414610fdd576040517fc561806500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080610fea848461228f565b91509150600080600080610ffd8661236f565b8b516020808e019190912063ffffffff8f167f0000000000000000000000000000000000000000000000000000000000000000831b67ffffffff0000000016176000908152915260408120959950929750909550935091905090506110658d8d8d8d856123d8565b600061107186866124f3565b90506000611089828663ffffffff8e1660011461266f565b909550905060008061109d8685898d6128bf565b909250905034156110b1576110b1866129d8565b6040805163ffffffff8b1681526001600160a01b03868116602083015291810185905283821660608201526080810183905260a08101879052908716907f7864397c00beabf21ab17a04795e450354505d879a634dd2632f4fdc4b5ba04e9060c00160405180910390a25050505050505050505050505050505050565b6000546001600160a01b031633146111885760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b6001600160a01b0385166111c8576040517f76998feb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111d3600886612a72565b611209576040517f1191732500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61121286612a87565b6001600160a01b0385166000908152600160209081526040909120875161123b92890190613a54565b508460028760405161124d9190614171565b90815260405190819003602001902080546001600160a01b03929092167fffffffffffffffffffffffff00000000000000000000000000000000000000009092169190911790556112a18585858585612c27565b505050505050565b6000546001600160a01b031633146113035760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b61130e600886611e6e565b611344576040517f53b5a66c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113518585858585612c27565b5050505050565b6000546001600160a01b031633146113b25760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b6113bd600882612e1d565b6113f3576040517f53b5a66c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116600090815260016020526040812080546114169061408e565b80601f01602080910402602001604051908101604052809291908181526020018280546114429061408e565b801561148f5780601f106114645761010080835404028352916020019161148f565b820191906000526020600020905b81548152906001019060200180831161147257829003601f168201915b505050506001600160a01b03841660009081526001602052604081209293506114b9929150613ad4565b6002816040516114c99190614171565b908152604080516020928190038301902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556001600160a01b0393909316600090815260039091529182209190915550565b6000546001600160a01b0316331461157a5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b6115846000612e32565b565b6000546001600160a01b031633146115e05760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b6115f060026402540be4006141bc565b811115611629576040517f28562c4700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60068190556040518181527fd10d75876659a287a59a6ccfa2e3fff42f84d94b542837acd30bc184d562de40906020015b60405180910390a150565b6000806116723084612e9a565b6001600160a01b03163b1192915050565b606060006116916008612f86565b90508067ffffffffffffffff8111156116ac576116ac613c57565b6040519080825280602002602001820160405280156116f257816020015b6040805180820190915260608152600060208201528152602001906001900390816116ca5790505b50915060005b8181101561180b57600061170d600883612f90565b9050604051806040016040528060016000846001600160a01b03166001600160a01b03168152602001908152602001600020805461174a9061408e565b80601f01602080910402602001604051908101604052809291908181526020018280546117769061408e565b80156117c35780601f10611798576101008083540402835291602001916117c3565b820191906000526020600020905b8154815290600101906020018083116117a657829003601f168201915b50505050508152602001826001600160a01b03168152508483815181106117ec576117ec6141f7565b602002602001018190525050808061180390614226565b9150506116f8565b505090565b3360008181526005602090815260409182902080547fffffffffffffffffffffffff000000000000000000000000000000000000000081166001600160a01b03878116918217909355845192909116808352928201529092917f9dfcadd14a1ddfb19c51e84b87452ca32a43c5559e9750d1575c77105cdeac1e910160405180910390a25050565b6000546001600160a01b031633146118f25760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b60078190556040518181527f5e8bad84cb22c143a6757c7f1252a7d53493816880330977cc99bb7c15aaf6b49060200161165a565b6000546001600160a01b031633146119815760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b611584612f9c565b6000546001600160a01b031633146119e35760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b61158461305f565b6000546001600160a01b03163314611a455760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b821580611a5157504683145b15611a88576040517f3f8f40a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000063ffffffff168263ffffffff1603611aed576040517f93c970c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8216156001841414611b30576040517f93c970c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116611b70576040517f24305eca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051808201825263ffffffff93841681526001600160a01b0392831660208083019182526000968752600b905291909420935184549151909216640100000000027fffffffffffffffff0000000000000000000000000000000000000000000000009091169190921617179055565b6000546001600160a01b03163314611c3b5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092c565b6001600160a01b038116611cb75760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161092c565b610f9f81612e32565b3360009081526004602090815260408083206001600160a01b038516845290915281205490819003611d1e576040517f30b93f1d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360008181526004602090815260408083206001600160a01b0387168085529252822091909155610b0b9183611e90565b6000611d5b83836124f3565b90505b92915050565b6001600160a01b03831660009081526003602090815260408083208151608081018352905464ffffffffff811680835268ffffffffffffffffff6501000000000083048116958401959095526e010000000000000000000000000000820485169383019390935277010000000000000000000000000000000000000000000000900490921660608301526402540be40090611dff9086614240565b611e0991906141bc565b9150600083611e1c578160200151611e22565b81604001515b68ffffffffffffffffff16905080831015611e3b578092505b816060015168ffffffffffffffffff16831115611e6557816060015168ffffffffffffffffff1692505b50509392505050565b6001600160a01b03811660009081526001830160205260408120541515611d5b565b6040516001600160a01b038316602482015260448101829052611f399084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613100565b505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009081906001600160a01b038516906370a0823190602401602060405180830381865afa158015611fa0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fc4919061425f565b9050611fdb6001600160a01b0385163330866131e5565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015281906001600160a01b038616906370a0823190602401602060405180830381865afa15801561203a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061205e919061425f565b6120689190614278565b949350505050565b606060a08351146120ad576040517f74593f8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff84166120f8578151156120f1576040517f74593f8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50816108cf565b60001963ffffffff85160161216b576080825114612142576040517f74593f8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b828260405160200161215592919061428f565b60405160208183030381529060405290506108cf565b6040517f523fa8d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e90604401602060405180830381865afa158015612206573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061222a919061425f565b90508181101561226557801561224f5761224f6001600160a01b038516846000613236565b6122656001600160a01b03851684600019613236565b50505050565b6000611d5b6122836001600160a01b03851684612e9a565b6001600160a01b031690565b60608063ffffffff84166122f05760a08351146122d8576040517f74593f8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50506040805160208101909152600081528190612368565b60001963ffffffff85160161216b57608061230c60a0826142b4565b61231691906142b4565b83511461234f576040517f74593f8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b828060200190518101906123639190614311565b915091505b9250929050565b600080600080600060a08651146123b2576040517f74593f8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b858060200190518101906123c69190614375565b939a9299509097509550909350915050565b60006123e382613384565b905060006357ecfd2860e01b878787876040516024016124069493929190614407565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529050600061249d6001600160a01b0384167f000000000000000000000000000000000000000000000000000000000000000084613430565b9050808060200190518101906124b3919061442e565b6124e9576040517f182f34eb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050505050565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663cb75c11c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612554573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612578919061444b565b9050806001600160a01b03166378a0565e856125a3866001600160a01b03166001600160a01b031690565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815263ffffffff9290921660048301526024820152604401602060405180830381865afa158015612602573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612626919061444b565b91506001600160a01b038216612668576040517f53b5a66c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5092915050565b60008061267d600886611e6e565b6126b3576040517f53b5a66c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6126be858585611d64565b90508381106126f9576040517f3eae42e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360009081526005602052604090205481850392506001600160a01b0316806127ae576001600160a01b03861660009081527f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ec6020526040812080548492906127639084906142b4565b909155505060408051600080825260208201529081018390527f108516ddcf5ba43cea6bb2cd5ff6d59ac196c1c86ccb9178332b9dd72d1ca5619060600160405180910390a16128b6565b60006402540be400600654846127c49190614240565b6127ce91906141bc565b905060006127dc8285614278565b6001600160a01b03891660009081527f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ec60205260408120805492935084929091906128289084906142b4565b90915550506001600160a01b038084166000908152600460209081526040808320938c16835292905290812080548392906128649084906142b4565b9091555050604080516001600160a01b0385168152602081018390529081018390527f108516ddcf5ba43cea6bb2cd5ff6d59ac196c1c86ccb9178332b9dd72d1ca5619060600160405180910390a150505b50935093915050565b60008082516000036128e9576128df6001600160a01b0386168786611e90565b50839050826129cf565b6001600160a01b038086166000908152600c602052604090205416806129295761291d6001600160a01b0387168887611e90565b858592509250506129cf565b6000806000806129388861343f565b935093509350935061294a85846134a3565b96506001600160a01b03871661297e5761296e6001600160a01b038b168c8b611e90565b89899650965050505050506129cf565b6129898a868b61219d565b6129978585858c86866135a6565b9550856000036129b55761296e6001600160a01b038b168c8b611e90565b6129c96001600160a01b0388168c88611e90565b50505050505b94509492505050565b6000816001600160a01b03163460405160006040518083038185875af1925050503d8060008114612a25576040519150601f19603f3d011682016040523d82523d6000602084013e612a2a565b606091505b505090507ff9b0951a3a6282341e1ba9414555d42d04e99076337702ee6dc484a706bfd68381612a5b576000612a5d565b345b60405190815260200160405180910390a15050565b6000611d5b836001600160a01b03841661365d565b60006001600160a01b0316600282604051612aa29190614171565b908152604051908190036020019020546001600160a01b031614612af2576040517f82ca3adf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80518190600510612b2f576040517f3f8fe5a800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b6005811015611f39576040518060400160405280600581526020017f434354502e0000000000000000000000000000000000000000000000000000008152508181518110612b8257612b826141f7565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916828281518110612bc157612bc16141f7565b01602001517fff000000000000000000000000000000000000000000000000000000000000001614612c1f576040517f3f8fe5a800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600101612b32565b62989680841115612c64576040517f76998feb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81831115612c9e576040517f76998feb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80821115612cd8576040517f76998feb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518060800160405280612cec866136ac565b64ffffffffff168152602001612d01856136f1565b68ffffffffffffffffff168152602001612d1a846136f1565b68ffffffffffffffffff168152602001612d33836136f1565b68ffffffffffffffffff9081169091526001600160a01b039096166000908152600360209081526040918290208351815492850151938501516060909501518a16770100000000000000000000000000000000000000000000000276ffffffffffffffffffffffffffffffffffffffffffffff958b166e01000000000000000000000000000002959095166dffffffffffffffffffffffffffff94909a1665010000000000027fffffffffffffffffffffffffffffffffffff000000000000000000000000000090931664ffffffffff909116179190911791909116969096171790945550505050565b6000611d5b836001600160a01b038416613736565b600080546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000611d5b83836040518060400160405280602081526020017f602036038060203d373d3d3d923d343d355af13d82803e903d91601e57fd5bf3815250604051602001612ee79190614468565b60405160208183030381529060405280519060200120604051602001612f6d939291907fff00000000000000000000000000000000000000000000000000000000000000815260609390931b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660018401526015830191909152603582015260550190565b6040516020818303038152906040528051906020012090565b6000611d5e825490565b6000611d5b8383613829565b600a5460ff1615612fef5760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a2070617573656400000000000000000000000000000000604482015260640161092c565b600a80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586130423390565b6040516001600160a01b03909116815260200160405180910390a1565b600a5460ff166130b15760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f7420706175736564000000000000000000000000604482015260640161092c565b600a80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33613042565b6000613155826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166138539092919063ffffffff16565b805190915015611f395780806020019051810190613173919061442e565b611f395760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161092c565b6040516001600160a01b03808516602483015283166044820152606481018290526122659085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401611ed5565b8015806132c957506040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa1580156132a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132c7919061425f565b155b61333b5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e636500000000000000000000606482015260840161092c565b6040516001600160a01b038316602482015260448101829052611f399084907f095ea7b30000000000000000000000000000000000000000000000000000000090606401611ed5565b6000806040518060400160405280602081526020017f602036038060203d373d3d3d923d343d355af13d82803e903d91601e57fd5bf38152506040516020016133cd9190614468565b6040516020818303038152906040529050828151602083016000f591506001600160a01b03821661342a576040517f27afa9fc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50919050565b60606108cc8484846000613862565b6000806000806080855114613480576040517f74593f8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8480602001905181019061349491906144e5565b93509350935093509193509193565b6040805160ff831660248083019190915282518083039091018152604490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f82b86600000000000000000000000000000000000000000000000000000000001790529051600091829182916001600160a01b0387169161352b9190614171565b600060405180830381855afa9150503d8060008114613566576040519150601f19603f3d011682016040523d82523d6000602084013e61356b565b606091505b509150915081801561357e575080516020145b156135995761359261358f82614528565b90565b925061359e565b600092505b505092915050565b6040517f9169558600000000000000000000000000000000000000000000000000000000815260ff8087166004830152851660248201526044810184905260648101829052608481018390526000906001600160a01b0388169063916955869060a4016020604051808303816000875af1925050508015613644575060408051601f3d908101601f191682019092526136419181019061425f565b60015b61365057506000613653565b90505b9695505050505050565b60008181526001830160205260408120546136a457508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155611d5e565b506000611d5e565b600064ffffffffff8211156136ed576040517fe58d471800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090565b600068ffffffffffffffffff8211156136ed576040517fe58d471800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818152600183016020526040812054801561381f57600061375a600183614278565b855490915060009061376e90600190614278565b90508181146137d357600086600001828154811061378e5761378e6141f7565b90600052602060002001549050808760000184815481106137b1576137b16141f7565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806137e4576137e461454c565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050611d5e565b6000915050611d5e565b6000826000018281548110613840576138406141f7565b9060005260206000200154905092915050565b60606108cc84846000856138ad565b60606138a46001600160a01b0385168460405160200161388392919061457b565b60408051601f198184030181529190526001600160a01b03871690846139f5565b95945050505050565b6060824710156139255760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c0000000000000000000000000000000000000000000000000000606482015260840161092c565b6001600160a01b0385163b61397c5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161092c565b600080866001600160a01b031685876040516139989190614171565b60006040518083038185875af1925050503d80600081146139d5576040519150601f19603f3d011682016040523d82523d6000602084013e6139da565b606091505b50915091506139ea828286613a1b565b979650505050505050565b60606108cc8484846040518060600160405280602981526020016145a2602991396138ad565b60608315613a2a5750816108cf565b825115613a3a5782518084602001fd5b8160405162461bcd60e51b815260040161092c9190613ba9565b828054613a609061408e565b90600052602060002090601f016020900481019282613a825760008555613ac8565b82601f10613a9b57805160ff1916838001178555613ac8565b82800160010185558215613ac8579182015b82811115613ac8578251825591602001919060010190613aad565b506136ed929150613b0a565b508054613ae09061408e565b6000825580601f10613af0575050565b601f016020900490600052602060002090810190610f9f91905b5b808211156136ed5760008155600101613b0b565b6001600160a01b0381168114610f9f57600080fd5b600060208284031215613b4657600080fd5b81356108cf81613b1f565b60005b83811015613b6c578181015183820152602001613b54565b838111156122655750506000910152565b60008151808452613b95816020860160208601613b51565b601f01601f19169290920160200192915050565b602081526000611d5b6020830184613b7d565b8015158114610f9f57600080fd5b600080600060608486031215613bdf57600080fd5b8335613bea81613b1f565b9250602084013591506040840135613c0181613bbc565b809150509250925092565b60008060408385031215613c1f57600080fd5b8235613c2a81613b1f565b91506020830135613c3a81613b1f565b809150509250929050565b63ffffffff81168114610f9f57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715613caf57613caf613c57565b604052919050565b600067ffffffffffffffff821115613cd157613cd1613c57565b50601f01601f191660200190565b600082601f830112613cf057600080fd5b8135613d03613cfe82613cb7565b613c86565b818152846020838601011115613d1857600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060c08789031215613d4e57600080fd5b8635613d5981613b1f565b9550602087013594506040870135613d7081613b1f565b9350606087013592506080870135613d8781613c45565b915060a087013567ffffffffffffffff811115613da357600080fd5b613daf89828a01613cdf565b9150509295509295509295565b60008083601f840112613dce57600080fd5b50813567ffffffffffffffff811115613de657600080fd5b60208301915083602082850101111561236857600080fd5b60008060008060008060808789031215613e1757600080fd5b863567ffffffffffffffff80821115613e2f57600080fd5b613e3b8a838b01613dbc565b90985096506020890135915080821115613e5457600080fd5b613e608a838b01613dbc565b909650945060408901359150613e7582613c45565b90925060608801359080821115613e8b57600080fd5b50613daf89828a01613cdf565b60008060008060008060c08789031215613eb157600080fd5b863567ffffffffffffffff811115613ec857600080fd5b613ed489828a01613cdf565b9650506020870135613ee581613b1f565b95989597505050506040840135936060810135936080820135935060a0909101359150565b600080600080600060a08688031215613f2257600080fd5b8535613f2d81613b1f565b97602087013597506040870135966060810135965060800135945092505050565b600060208284031215613f6057600080fd5b5035919050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b83811015613ff6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc089840301855281518051878552613fd088860182613b7d565b918901516001600160a01b03169489019490945294870194925090860190600101613f8e565b509098975050505050505050565b60006020828403121561401657600080fd5b813567ffffffffffffffff81111561402d57600080fd5b61206884828501613cdf565b60008060006060848603121561404e57600080fd5b83359250602084013561406081613c45565b91506040840135613c0181613b1f565b6000806040838503121561408357600080fd5b8235613c2a81613c45565b600181811c908216806140a257607f821691505b60208210810361342a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b805167ffffffffffffffff811681146140f357600080fd5b919050565b60006020828403121561410a57600080fd5b611d5b826140db565b87815267ffffffffffffffff871660208201526001600160a01b038616604082015284606082015263ffffffff8416608082015260e060a0820152600061415d60e0830185613b7d565b90508260c083015298975050505050505050565b60008251614183818460208701613b51565b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000826141f2577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060001982036142395761423961418d565b5060010190565b600081600019048311821515161561425a5761425a61418d565b500290565b60006020828403121561427157600080fd5b5051919050565b60008282101561428a5761428a61418d565b500390565b6040815260006142a26040830185613b7d565b82810360208401526138a48185613b7d565b600082198211156142c7576142c761418d565b500190565b600082601f8301126142dd57600080fd5b81516142eb613cfe82613cb7565b81815284602083860101111561430057600080fd5b612068826020830160208701613b51565b6000806040838503121561432457600080fd5b825167ffffffffffffffff8082111561433c57600080fd5b614348868387016142cc565b9350602085015191508082111561435e57600080fd5b5061436b858286016142cc565b9150509250929050565b600080600080600060a0868803121561438d57600080fd5b855161439881613c45565b94506143a6602087016140db565b935060408601516143b681613b1f565b6060870151608088015191945092506143ce81613b1f565b809150509295509295909350565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b60408152600061441b6040830186886143dc565b82810360208401526139ea8185876143dc565b60006020828403121561444057600080fd5b81516108cf81613bbc565b60006020828403121561445d57600080fd5b81516108cf81613b1f565b7f7f000000000000000000000000000000000000000000000000000000000000008152600082516144a0816001850160208701613b51565b7f3d5260203df300000000000000000000000000000000000000000000000000006001939091019283015250600701919050565b805160ff811681146140f357600080fd5b600080600080608085870312156144fb57600080fd5b614504856144d4565b9350614512602086016144d4565b6040860151606090960151949790965092505050565b8051602080830151919081101561342a5760001960209190910360031b1b16919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b82815260008251614593816020850160208701613b51565b91909101602001939250505056fe416464726573733a206c6f772d6c6576656c2063616c6c20776974682076616c7565206661696c6564a2646970667358221220599a9c446de9cb152ce66f97e0b3fbf4a2a0aebd13e35d04745bca99d587c69064736f6c634300080d0033","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"94722:18505:0:-:0;;;96009:295;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;28929:32;28025:10;28929:18;:32::i;:::-;93471:7;:15;;-1:-1:-1;;93471:15:0;;;-1:-1:-1;;;;;96080:32:0;;;;;;96163:41;;;-1:-1:-1;;;96163:41:0;;;;:39;;:41;;;;;;;;;;;;;;;96080:32;96163:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;96122:83:0;;;;;96229:32;;;-1:-1:-1;;;96229:32:0;;;;:30;;:32;;;;;;;;;;;;;;;96122:83;96229:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;96215:46;;;;96271:26;96290:6;96271:18;:26::i;:::-;96009:295;;94722:18505;;30278:187;30351:16;30370:6;;-1:-1:-1;;;;;30386:17:0;;;-1:-1:-1;;;;;;30386:17:0;;;;;;30418:40;;30370:6;;;;;;;30418:40;;30351:16;30418:40;30341:124;30278:187;:::o;14:148:1:-;-1:-1:-1;;;;;106:31:1;;96:42;;86:70;;152:1;149;142:12;86:70;14:148;:::o;167:442::-;269:6;277;330:2;318:9;309:7;305:23;301:32;298:52;;;346:1;343;336:12;298:52;378:9;372:16;397:48;439:5;397:48;:::i;:::-;514:2;499:18;;493:25;464:5;;-1:-1:-1;527:50:1;493:25;527:50;:::i;:::-;596:7;586:17;;;167:442;;;;;:::o;614:268::-;684:6;737:2;725:9;716:7;712:23;708:32;705:52;;;753:1;750;743:12;705:52;785:9;779:16;804:48;846:5;804:48;:::i;:::-;871:5;614:268;-1:-1:-1;;;614:268:1:o;887:280::-;956:6;1009:2;997:9;988:7;984:23;980:32;977:52;;;1025:1;1022;1015:12;977:52;1057:9;1051:16;1107:10;1100:5;1096:22;1089:5;1086:33;1076:61;;1133:1;1130;1123:12;887:280;94722:18505:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","srcMapRuntime":"94722:18505:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;44577:47;;;;;;;;;;-1:-1:-1;44577:47:0;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;51859:204;;;;;;;;;;-1:-1:-1;51859:204:0;;;;;:::i;:::-;;:::i;:::-;;;1959:25:1;;;1947:2;1932:18;51859:204:0;1813:177:1;97652:361:0;;;;;;;;;;-1:-1:-1;97652:361:0;;;;;:::i;:::-;;:::i;:::-;;98845:289;;;;;;;;;;-1:-1:-1;98845:289:0;;;;;:::i;:::-;;:::i;99870:2524::-;;;;;;;;;;-1:-1:-1;99870:2524:0;;;;;:::i;:::-;;:::i;48286:177::-;;;;;;;;;;;;;:::i;45364:55::-;;;;;;;;;;-1:-1:-1;45364:55:0;;;;;:::i;:::-;;;;;;;;;;;;-1:-1:-1;;;;;45364:55:0;;;;;;-1:-1:-1;;;;;4802:55:1;;;4784:74;;4772:2;4757:18;45364:55:0;4638:226:1;95725:47:0;;;;;;;;;;;;;;;102433:1776;;;;;;:::i;:::-;;:::i;46842:772::-;;;;;;;;;;-1:-1:-1;46842:772:0;;;;;:::i;:::-;;:::i;49313:329::-;;;;;;;;;;-1:-1:-1;49313:329:0;;;;;:::i;:::-;;:::i;93588:84::-;;;;;;;;;;-1:-1:-1;93658:7:0;;;;93588:84;;;7956:14:1;;7949:22;7931:41;;7919:2;7904:18;93588:84:0;7791:187:1;47743:475:0;;;;;;;;;;-1:-1:-1;47743:475:0;;;;;:::i;:::-;;:::i;29676:101::-;;;;;;;;;;;;;:::i;49871:243::-;;;;;;;;;;-1:-1:-1;49871:243:0;;;;;:::i;:::-;;:::i;95664:55::-;;;;;;;;;;;;;;;95623:35;;;;;;;;;;;;;;;;;;8601:10:1;8589:23;;;8571:42;;8559:2;8544:18;95623:35:0;8427:192:1;29044:85:0;;;;;;;;;;-1:-1:-1;29090:7:0;29116:6;-1:-1:-1;;;;;29116:6:0;29044:85;;104870:285;;;;;;;;;;-1:-1:-1;104870:285:0;;;;;:::i;:::-;;:::i;52152:386::-;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;50814:265::-;;;;;;;;;;-1:-1:-1;50814:265:0;;;;;:::i;:::-;;:::i;95952:50::-;;;;;;;;;;-1:-1:-1;95952:50:0;;;;;:::i;:::-;;;;;;;;;;;;-1:-1:-1;;;;;95952:50:0;;;44697:47;;;;;;;;;;-1:-1:-1;44697:47:0;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;44697:47:0;;;45613:26;;;;;;;;;;;;;;;;48582:179;;;;;;;;;;-1:-1:-1;48582:179:0;;;;;:::i;:::-;;:::i;45092:70::-;;;;;;;;;;-1:-1:-1;45092:70:0;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;98163:68;;;;;;;;;;;;;:::i;44816:48::-;;;;;;;;;;-1:-1:-1;44816:48:0;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10563:12:1;10551:25;;;10533:44;;10596:20;10652:15;;;10647:2;10632:18;;10625:43;10704:15;;;10684:18;;;10677:43;;;;10756:15;10751:2;10736:18;;10729:43;10520:3;10505:19;44816:48:0;10310:468:1;45757:29:0;;;;;;;;;;;;;;;;98383:72;;;;;;;;;;;;;:::i;95833:58::-;;;;;;;;;;-1:-1:-1;95833:58:0;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;95833:58:0;;;;;;;10985:10:1;10973:23;;;10955:42;;-1:-1:-1;;;;;11033:55:1;;;11028:2;11013:18;;11006:83;10928:18;95833:58:0;10783:312:1;96721:857:0;;;;;;;;;;-1:-1:-1;96721:857:0;;;;;:::i;:::-;;:::i;29926:198::-;;;;;;;;;;-1:-1:-1;29926:198:0;;;;;:::i;:::-;;:::i;99229:278::-;;;;;;;;;;-1:-1:-1;99229:278:0;;;;;:::i;:::-;;:::i;104636:162::-;;;;;;;;;;-1:-1:-1;104636:162:0;;;;;:::i;:::-;;:::i;44577:47::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;51859:204::-;51984:11;52014:42;52034:5;52041:6;52049;52014:19;:42::i;:::-;52007:49;;51859:204;;;;;;:::o;97652:361::-;29090:7;29116:6;-1:-1:-1;;;;;29116:6:0;28025:10;29256:23;29248:68;;;;-1:-1:-1;;;29248:68:0;;12594:2:1;29248:68:0;;;12576:21:1;;;12613:18;;;12606:30;12672:34;12652:18;;;12645:62;12724:18;;29248:68:0;;;;;;;;;-1:-1:-1;;;;;97748:25:0;::::1;97744:55;;97782:17;;;;;;;;;;;;;;97744:55;97814:35;:13;97837:11:::0;97814:22:::1;:35::i;:::-;97809:68;;97858:19;;;;;;;;;;;;;;97809:68;-1:-1:-1::0;;;;;97971:28:0;;::::1;;::::0;;;:15:::1;:28;::::0;;;;:35;;;::::1;::::0;;;::::1;;::::0;;97652:361::o;98845:289::-;29090:7;29116:6;-1:-1:-1;;;;;29116:6:0;28025:10;29256:23;29248:68;;;;-1:-1:-1;;;29248:68:0;;12594:2:1;29248:68:0;;;12576:21:1;;;12613:18;;;12606:30;12672:34;12652:18;;;12645:62;12724:18;;29248:68:0;12392:356:1;29248:68:0;-1:-1:-1;;;;;98937:34:0;::::1;98919:15;98937:34:::0;;;:27;::::1;:34:::0;:27;:34;;;;98985:12;;;98981:41:::1;;99006:16;;;;;;;;;;;;;;98981:41;-1:-1:-1::0;;;;;99032:34:0;::::1;99069:1;99032:34:::0;;;:27;::::1;:34:::0;:27;:34;;:38;99080:47:::1;::::0;99107:10:::1;99119:7:::0;99080:26:::1;:47::i;:::-;98909:225;98845:289:::0;:::o;99870:2524::-;93658:7;;;;93901:9;93893:38;;;;-1:-1:-1;;;93893:38:0;;12955:2:1;93893:38:0;;;12937:21:1;12994:2;12974:18;;;12967:30;13033:18;13013;;;13006:46;13069:18;;93893:38:0;12753:340:1;93893:38:0;100173:33:::1;:13;100196:9:::0;100173:22:::1;:33::i;:::-;100168:66;;100215:19;;;;;;;;;;;;;;100168:66;100332:29;100343:9;100354:6;100332:10;:29::i;:::-;100323:38;;100371:12;100386:18;-1:-1:-1::0;;;;;100386:37:0::1;;:39;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;66000:67:::0;;;18622:10:1;100674:11:0::1;18610:23:1::0;66000:67:0;;;18592:42:1;18682:18;18670:31;;18650:18;;;18643:59;-1:-1:-1;;;;;18799:15:1;;;65952:29:0;18779:18:1;;18772:43;18831:18;;;18824:34;;;18895:15;;18874:19;;;;18867:44;;;;66000:67:0;;;;;;;;;;18564:19:1;;;;66000:67:0;;;100371:54;;-1:-1:-1;100547:29:0::1;::::0;100579:178:::1;::::0;100617:14;;100737:10:::1;100579:24;:178::i;:::-;100767:26;100796:27:::0;;;:18:::1;:27;::::0;;;;;;;100767:56;;;;::::1;::::0;;;;::::1;::::0;::::1;::::0;;;;::::1;-1:-1:-1::0;;;;;100767:56:0::1;::::0;;::::1;::::0;;;100547:210;;-1:-1:-1;100767:56:0;100909:19;;;100905:60:::1;;100937:28;;;;;;;;;;;;;;100905:60;101002:13:::0;;112723:27;;112671:2;112723:27;;;;;;;112641:32;;;;;112640:51;;;;100975:24:::1;113058:17:::0;;;113088:23;;113206:2;113193:16;;101547:57:::1;101561:9:::0;101580:14:::1;101597:6:::0;101547:13:::1;:57::i;:::-;-1:-1:-1::0;;;;;101614:14:0::1;:39;;101667:6:::0;101687:17;101718:14;101746:9;101769:64:::1;101718:14:::0;101823:9:::1;101769:18;:64::i;:::-;101614:229;::::0;;::::1;::::0;;;;;;::::1;::::0;::::1;13746:25:1::0;;;;101614:229:0::1;13807:23:1::0;;;;13787:18;;;13780:51;13847:18;;;13840:34;;;;-1:-1:-1;;;;;13910:55:1;13890:18;;;13883:83;13982:19;;;13975:35;13718:19;;101614:229:0::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;102225:9;-1:-1:-1::0;;;;;102173:214:0::1;;102204:7;102248:5;102267:9;102290:6;102310:14;102338:16;102368:9;102173:214;;;;;;;;;;;;:::i;:::-;;;;;;;;100091:2303;;;;;;99870:2524:::0;;;;;;:::o;48286:177::-;29090:7;29116:6;-1:-1:-1;;;;;29116:6:0;28025:10;29256:23;29248:68;;;;-1:-1:-1;;;29248:68:0;;12594:2:1;29248:68:0;;;12576:21:1;;;12613:18;;;12606:30;12672:34;12652:18;;;12645:62;12724:18;;29248:68:0;12392:356:1;29248:68:0;48355:49:::1;::::0;48337:12:::1;::::0;48355:10:::1;::::0;48378:21:::1;::::0;48337:12;48355:49;48337:12;48355:49;48378:21;48355:10;:49:::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;48336:68;;;48419:7;48414:42;;48435:21;;;;;;;;;;;;;;48414:42;48326:137;48286:177::o:0;102433:1776::-;102710:14;;102697:9;:27;102693:64;;102733:24;;;;;;;;;;;;;;102693:64;102768:24;102794:23;102821:92;102859:14;102887:16;102821:24;:92::i;:::-;102767:146;;;;102924:19;102947:23;102972:14;102988:17;103009:54;103051:11;103009:41;:54::i;:::-;112723:27;;112671:2;112723:27;;;;;;;112640:51;;;103315:11;112641:32;;;;112640:51;103284:17;113058;;;113088:23;;113206:2;113193:16;;102923:140;;-1:-1:-1;102923:140:0;;-1:-1:-1;102923:140:0;;-1:-1:-1;102923:140:0;-1:-1:-1;103284:17:0;113193:16;-1:-1:-1;103284:77:0;;103438:47;103455:7;;103464:9;;103475;103438:16;:47::i;:::-;103495:13;103511:45;103526:12;103540:15;103511:14;:45::i;:::-;103495:61;-1:-1:-1;103566:11:0;103673:74;103495:61;103697:6;103705:41;;;64436:1;103705:41;103673:16;:74::i;:::-;103657:90;;-1:-1:-1;103657:90:0;-1:-1:-1;103857:16:0;;103896:53;103912:9;103923:5;103657:90;103938:10;103896:15;:53::i;:::-;103856:93;;-1:-1:-1;103856:93:0;-1:-1:-1;104053:9:0;:13;104049:47;;104068:28;104086:9;104068:17;:28::i;:::-;104111:91;;;15287:10:1;15275:23;;15257:42;;-1:-1:-1;;;;;15396:15:1;;;15391:2;15376:18;;15369:43;15428:18;;;15421:34;;;15491:15;;;15486:2;15471:18;;15464:43;15538:3;15523:19;;15516:35;;;15582:3;15567:19;;15560:35;;;104111:91:0;;;;;;15244:3:1;15229:19;104111:91:0;;;;;;;102620:1589;;;;;;;;;;;102433:1776;;;;;;:::o;46842:772::-;29090:7;29116:6;-1:-1:-1;;;;;29116:6:0;28025:10;29256:23;29248:68;;;;-1:-1:-1;;;29248:68:0;;12594:2:1;29248:68:0;;;12576:21:1;;;12613:18;;;12606:30;12672:34;12652:18;;;12645:62;12724:18;;29248:68:0;12392:356:1;29248:68:0;-1:-1:-1;;;;;47060:19:0;::::1;47056:53;;47088:21;;;;;;;;;;;;;;47056:53;47227:24;:13;47245:5:::0;47227:17:::1;:24::i;:::-;47222:61;;47260:23;;;;;;;;;;;;;;47222:61;47368:27;47388:6;47368:19;:27::i;:::-;-1:-1:-1::0;;;;;47441:20:0;::::1;;::::0;;;:13:::1;:20;::::0;;;;;;;:29;;::::1;::::0;;::::1;::::0;::::1;:::i;:::-;;47504:5;47480:13;47494:6;47480:21;;;;;;:::i;:::-;::::0;;;::::1;::::0;;;;;::::1;::::0;;;:29;;-1:-1:-1;;;;;47480:29:0;;;::::1;::::0;;;::::1;::::0;;;::::1;::::0;;47544:63:::1;47557:5:::0;47564:10;47576;47588;47600:6;47544:12:::1;:63::i;:::-;46842:772:::0;;;;;;:::o;49313:329::-;29090:7;29116:6;-1:-1:-1;;;;;29116:6:0;28025:10;29256:23;29248:68;;;;-1:-1:-1;;;29248:68:0;;12594:2:1;29248:68:0;;;12576:21:1;;;12613:18;;;12606:30;12672:34;12652:18;;;12645:62;12724:18;;29248:68:0;12392:356:1;29248:68:0;49505:29:::1;:13;49528:5:::0;49505:22:::1;:29::i;:::-;49500:62;;49543:19;;;;;;;;;;;;;;49500:62;49572:63;49585:5;49592:10;49604;49616;49628:6;49572:12;:63::i;:::-;49313:329:::0;;;;;:::o;47743:475::-;29090:7;29116:6;-1:-1:-1;;;;;29116:6:0;28025:10;29256:23;29248:68;;;;-1:-1:-1;;;29248:68:0;;12594:2:1;29248:68:0;;;12576:21:1;;;12613:18;;;12606:30;12672:34;12652:18;;;12645:62;12724:18;;29248:68:0;12392:356:1;29248:68:0;47914:27:::1;:13;47935:5:::0;47914:20:::1;:27::i;:::-;47909:60;;47950:19;;;;;;;;;;;;;;47909:60;-1:-1:-1::0;;;;;48041:20:0;::::1;48018;48041::::0;;;:13:::1;:20;::::0;;;;48018:43;;::::1;::::0;::::1;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1::0;;;;;;;;;48078:20:0;::::1;;::::0;;;:13:::1;:20;::::0;;;;48018:43;;-1:-1:-1;48071:27:0::1;::::0;48078:20;-1:-1:-1;48071:27:0::1;:::i;:::-;48115:13;48129:6;48115:21;;;;;;:::i;:::-;::::0;;;::::1;::::0;;::::1;::::0;;;;;;;;48108:28;;;::::1;::::0;;-1:-1:-1;;;;;48191:20:0;;;::::1;-1:-1:-1::0;48191:20:0;;;:13:::1;:20:::0;;;;;;48184:27;;;;-1:-1:-1;47743:475:0:o;29676:101::-;29090:7;29116:6;-1:-1:-1;;;;;29116:6:0;28025:10;29256:23;29248:68;;;;-1:-1:-1;;;29248:68:0;;12594:2:1;29248:68:0;;;12576:21:1;;;12613:18;;;12606:30;12672:34;12652:18;;;12645:62;12724:18;;29248:68:0;12392:356:1;29248:68:0;29740:30:::1;29767:1;29740:18;:30::i;:::-;29676:101::o:0;49871:243::-;29090:7;29116:6;-1:-1:-1;;;;;29116:6:0;28025:10;29256:23;29248:68;;;;-1:-1:-1;;;29248:68:0;;12594:2:1;29248:68:0;;;12576:21:1;;;12613:18;;;12606:30;12672:34;12652:18;;;12645:62;12724:18;;29248:68:0;12392:356:1;29248:68:0;43871:19:::1;43889:1;43649:6;43871:19;:::i;:::-;49952:14;:33;49948:72;;;49994:26;;;;;;;;;;;;;;49948:72;50030:11;:28:::0;;;50073:34:::1;::::0;1959:25:1;;;50073:34:0::1;::::0;1947:2:1;1932:18;50073:34:0::1;;;;;;;;49871:243:::0;:::o;104870:285::-;104940:4;105147:1;105072:60;105115:4;105122:9;105072:34;:60::i;:::-;-1:-1:-1;;;;;105072:72:0;;:76;;104870:285;-1:-1:-1;;104870:285:0:o;52152:386::-;52202:33;52247:14;52264:22;:13;:20;:22::i;:::-;52247:39;;52329:6;52311:25;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;;;;;;;52311:25:0;;;;;;;;;;;;;;;;52296:40;;52351:9;52346:186;52370:6;52366:1;:10;52346:186;;;52397:13;52413:19;:13;52430:1;52413:16;:19::i;:::-;52397:35;;52464:57;;;;;;;;52485:13;:20;52499:5;-1:-1:-1;;;;;52485:20:0;-1:-1:-1;;;;;52485:20:0;;;;;;;;;;;;52464:57;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;52514:5;-1:-1:-1;;;;;52464:57:0;;;;52446:12;52459:1;52446:15;;;;;;;;:::i;:::-;;;;;;:75;;;;52383:149;52378:3;;;;;:::i;:::-;;;;52346:186;;;;52237:301;52152:386;:::o;50814:265::-;50927:10;50880:23;50906:32;;;:20;:32;;;;;;;;;;;50948:47;;;-1:-1:-1;;;;;50948:47:0;;;;;;;;;51010:62;;50906:32;;;;16979:34:1;;;17029:18;;;17022:43;50906:32:0;;50927:10;51010:62;;16891:18:1;51010:62:0;;;;;;;50870:209;50814:265;:::o;48582:179::-;29090:7;29116:6;-1:-1:-1;;;;;29116:6:0;28025:10;29256:23;29248:68;;;;-1:-1:-1;;;29248:68:0;;12594:2:1;29248:68:0;;;12576:21:1;;;12613:18;;;12606:30;12672:34;12652:18;;;12645:62;12724:18;;29248:68:0;12392:356:1;29248:68:0;48665:14:::1;:34:::0;;;48714:40:::1;::::0;1959:25:1;;;48714:40:0::1;::::0;1947:2:1;1932:18;48714:40:0::1;1813:177:1::0;98163:68:0;29090:7;29116:6;-1:-1:-1;;;;;29116:6:0;28025:10;29256:23;29248:68;;;;-1:-1:-1;;;29248:68:0;;12594:2:1;29248:68:0;;;12576:21:1;;;12613:18;;;12606:30;12672:34;12652:18;;;12645:62;12724:18;;29248:68:0;12392:356:1;29248:68:0;98216:8:::1;:6;:8::i;98383:72::-:0;29090:7;29116:6;-1:-1:-1;;;;;29116:6:0;28025:10;29256:23;29248:68;;;;-1:-1:-1;;;29248:68:0;;12594:2:1;29248:68:0;;;12576:21:1;;;12613:18;;;12606:30;12672:34;12652:18;;;12645:62;12724:18;;29248:68:0;12392:356:1;29248:68:0;98438:10:::1;:8;:10::i;96721:857::-:0;29090:7;29116:6;-1:-1:-1;;;;;29116:6:0;28025:10;29256:23;29248:68;;;;-1:-1:-1;;;29248:68:0;;12594:2:1;29248:68:0;;;12576:21:1;;;12613:18;;;12606:30;12672:34;12652:18;;;12645:62;12724:18;;29248:68:0;12392:356:1;29248:68:0;96963:18;;;:52:::1;;;97002:13;96985;:30;96963:52;96959:87;;;97024:22;;;;;;;;;;;;;;96959:87;97138:11;97122:27;;:12;:27;;::::0;97118:61:::1;;97158:21;;;;;;;;;;;;;;97118:61;97287:17;::::0;::::1;::::0;97327:1:::1;97310:18:::0;::::1;97286:43;97282:77;;97338:21;;;;;;;;;;;;;;97282:77;-1:-1:-1::0;;;;;97423:31:0;::::1;97419:61;;97463:17;;;;;;;;;;;;;;97419:61;97526:45;::::0;;;;::::1;::::0;;::::1;::::0;;::::1;::::0;;-1:-1:-1;;;;;97526:45:0;;::::1;;::::0;;::::1;::::0;;;-1:-1:-1;97490:33:0;;;:18:::1;:33:::0;;;;;;:81;;;;;;;;::::1;::::0;::::1;::::0;;;;;;;::::1;::::0;::::1;::::0;;96721:857::o;29926:198::-;29090:7;29116:6;-1:-1:-1;;;;;29116:6:0;28025:10;29256:23;29248:68;;;;-1:-1:-1;;;29248:68:0;;12594:2:1;29248:68:0;;;12576:21:1;;;12613:18;;;12606:30;12672:34;12652:18;;;12645:62;12724:18;;29248:68:0;12392:356:1;29248:68:0;-1:-1:-1;;;;;30014:22:0;::::1;30006:73;;;::::0;-1:-1:-1;;;30006:73:0;;17278:2:1;30006:73:0::1;::::0;::::1;17260:21:1::0;17317:2;17297:18;;;17290:30;17356:34;17336:18;;;17329:62;17427:8;17407:18;;;17400:36;17453:19;;30006:73:0::1;17076:402:1::0;30006:73:0::1;30089:28;30108:8;30089:18;:28::i;99229:278::-:0;99326:10;99292:15;99310:27;;;:15;:27;;;;;;;;-1:-1:-1;;;;;99310:34:0;;;;;;;;;;;99358:12;;;99354:41;;99379:16;;;;;;;;;;;;;;99354:41;99421:10;99442:1;99405:27;;;:15;:27;;;;;;;;-1:-1:-1;;;;;99405:34:0;;;;;;;;;:38;;;;99453:47;;99492:7;99453:26;:47::i;104636:162::-;104724:7;104750:41;104765:12;104779:11;104750:14;:41::i;:::-;104743:48;;104636:162;;;;;:::o;56945:559::-;-1:-1:-1;;;;;57124:20:0;;57071:11;57124:20;;;:13;:20;;;;;;;;57094:50;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;43649:6;;57197:32;;:6;:32;:::i;:::-;57196:52;;;;:::i;:::-;57190:58;;57287:14;57304:6;:58;;57339:12;:23;;;57304:58;;;57313:12;:23;;;57304:58;57287:75;;;;57382:6;57376:3;:12;57372:30;;;57396:6;57390:12;;57372:30;57451:12;:19;;;57445:25;;:3;:25;57441:56;;;57478:12;:19;;;57472:25;;;;57441:56;57084:420;;56945:559;;;;;:::o;38582:165::-;-1:-1:-1;;;;;38715:23:0;;38662:4;34249:19;;;:12;;;:19;;;;;;:24;;38685:55;34153:127;89358:205;89497:58;;-1:-1:-1;;;;;17908:55:1;;89497:58:0;;;17890:74:1;17980:18;;;17973:34;;;89470:86:0;;89490:5;;89520:23;;17863:18:1;;89497:58:0;;;;-1:-1:-1;;89497:58:0;;;;;;;;;;;;;;;;;;;;;;;;;;;89470:19;:86::i;:::-;89358:205;;;:::o;106167:324::-;106292:38;;;;;106324:4;106292:38;;;4784:74:1;106236:20:0;;;;-1:-1:-1;;;;;106292:23:0;;;;;4757:18:1;;106292:38:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;106268:62;-1:-1:-1;106340:65:0;-1:-1:-1;;;;;106340:30:0;;106371:10;106391:4;106398:6;106340:30;:65::i;:::-;106430:38;;;;;106462:4;106430:38;;;4784:74:1;106471:13:0;;-1:-1:-1;;;;;106430:23:0;;;;;4757:18:1;;106430:38:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:54;;;;:::i;:::-;106415:69;106167:324;-1:-1:-1;;;;106167:324:0:o;67415:840::-;67565:29;64544:6;67610:11;:18;:41;67606:78;;67660:24;;;;;;;;;;;;;;67606:78;67698:30;;;67694:555;;67748:17;;:22;67744:59;;67779:24;;;;;;;;;;;;;;67744:59;-1:-1:-1;67899:11:0;67892:18;;67694:555;-1:-1:-1;;67931:30:0;;;;67927:322;;64658:6;67981:10;:17;:39;67977:76;;68029:24;;;;;;;;;;;;;;67977:76;68153:11;68166:10;68142:35;;;;;;;;;:::i;:::-;;;;;;;;;;;;;68135:42;;;;67927:322;68215:23;;;;;;;;;;;;;;105667:448;105807:47;;;;;105839:4;105807:47;;;16979:34:1;-1:-1:-1;;;;;17049:15:1;;;17029:18;;;17022:43;105787:17:0;;105807:23;;;;;;16891:18:1;;105807:47:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;105787:67;;105880:6;105868:9;:18;105864:245;;;105978:14;;105974:57;;105994:37;-1:-1:-1;;;;;105994:25:0;;106020:7;106029:1;105994:25;:37::i;:::-;106045:53;-1:-1:-1;;;;;106045:25:0;;106071:7;-1:-1:-1;;106045:25:0;:53::i;:::-;105777:338;105667:448;;;:::o;111944:342::-;112035:7;112223:56;:37;-1:-1:-1;;;;;112223:26:0;;112250:9;112223:26;:37::i;:::-;-1:-1:-1;;;;;112223:54:0;;18982:127;70974:658;71098:24;;71167:30;;;71163:463;;64544:6;71217:16;:23;:46;71213:83;;71272:24;;;;;;;;;;;;;;71213:83;-1:-1:-1;;71310:29:0;;;;;;;;;-1:-1:-1;71310:29:0;;71318:16;;71310:29;;71163:463;-1:-1:-1;;71360:30:0;;;;71356:270;;64658:6;64873:28;64544:6;64658;64873:28;:::i;:::-;:49;;;;:::i;:::-;71410:16;:23;:46;71406:83;;71465:24;;;;;;;;;;;;;;71406:83;71521:16;71510:44;;;;;;;;;;;;:::i;:::-;71503:51;;;;71356:270;70974:658;;;;;:::o;69143:449::-;69256:19;69289:12;69315:23;69352:14;69380:17;64544:6;69426:11;:18;:41;69422:78;;69476:24;;;;;;;;;;;;;;69422:78;69528:11;69517:68;;;;;;;;;;;;:::i;:::-;69510:75;;;;-1:-1:-1;69510:75:0;;-1:-1:-1;69510:75:0;-1:-1:-1;69510:75:0;;-1:-1:-1;69143:449:0;-1:-1:-1;;69143:449:0:o;106592:981::-;106843:17;106863:37;106890:9;106863:26;:37::i;:::-;106843:57;;106961:20;107007:43;;;107052:7;;107061:9;;106984:87;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;106984:87:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;107328:59:0;-1:-1:-1;;;;;107328:21:0;;107358:18;106984:87;107328:21;:59::i;:::-;107302:85;;107514:10;107503:30;;;;;;;;;;;;:::i;:::-;107498:68;;107542:24;;;;;;;;;;;;;;107498:68;106726:847;;;106592:981;;;;;:::o;110552:398::-;110641:13;110666:19;110701:14;-1:-1:-1;;;;;110701:26:0;;:28;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;110666:64;;110748:6;-1:-1:-1;;;;;110748:20:0;;110769:12;110783:30;:11;-1:-1:-1;;;;;110783:28:0;-1:-1:-1;;;;;19079:22:0;;18982:127;110783:30;110748:66;;;;;;;;;;;22581:23:1;;;;110748:66:0;;;22563:42:1;22621:18;;;22614:34;22536:18;;110748:66:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;110740:74;-1:-1:-1;;;;;;110896:19:0;;110892:51;;110924:19;;;;;;;;;;;;;;110892:51;110656:294;110552:398;;;;:::o;53035:1335::-;53153:22;;53205:29;:13;53228:5;53205:22;:29::i;:::-;53200:62;;53243:19;;;;;;;;;;;;;;53200:62;53278:42;53298:5;53305:6;53313;53278:19;:42::i;:::-;53272:48;;53341:6;53334:3;:13;53330:50;;53356:24;;;;;;;;;;;;;;53330:50;53650:10;53606:20;53629:32;;;:20;:32;;;;;;53512:12;;;;-1:-1:-1;;;;;;53629:32:0;;53671:693;;-1:-1:-1;;;;;53805:34:0;;:27;:34;;;:27;;:34;:27;:34;;:41;;53843:3;;53805:27;:41;;53843:3;;53805:41;:::i;:::-;;;;-1:-1:-1;;53865:32:0;;;53886:1;22869:74:1;;;22974:2;22959:18;;22952:34;23002:18;;;22995:34;;;53865:32:0;;22857:2:1;22842:18;53865:32:0;;;;;;;53671:693;;;54003:25;43649:6;54038:11;;54032:3;:17;;;;:::i;:::-;54031:37;;;;:::i;:::-;54003:65;-1:-1:-1;54082:24:0;54109:23;54003:65;54109:3;:23;:::i;:::-;-1:-1:-1;;;;;54146:34:0;;:27;:34;;;:27;;:34;:27;:34;;:55;;54082:50;;-1:-1:-1;54184:17:0;;54146:34;;:27;:55;;54184:17;;54146:55;:::i;:::-;;;;-1:-1:-1;;;;;;;54215:29:0;;;;;;;:15;:29;;;;;;;;:36;;;;;;;;;;;:56;;54255:16;;54215:29;:56;;54255:16;;54215:56;:::i;:::-;;;;-1:-1:-1;;54290:63:0;;;-1:-1:-1;;;;;22887:55:1;;22869:74;;22974:2;22959:18;;22952:34;;;23002:18;;;22995:34;;;54290:63:0;;22857:2:1;22842:18;54290:63:0;;;;;;;53914:450;;53671:693;53190:1180;53035:1335;;;;;;:::o;107769:1721::-;107925:16;107943:17;108043:10;:17;108064:1;108043:22;108039:134;;108081:45;-1:-1:-1;;;;;108081:26:0;;108108:9;108119:6;108081:26;:45::i;:::-;-1:-1:-1;108148:5:0;;-1:-1:-1;108155:6:0;108140:22;;108039:134;-1:-1:-1;;;;;108345:22:0;;;108330:12;108345:22;;;:15;:22;;;;;;;;108433:130;;108471:45;-1:-1:-1;;;;;108471:26:0;;108498:9;108509:6;108471:26;:45::i;:::-;108538:5;108545:6;108530:22;;;;;;;108433:130;108573:20;108595:18;108615:16;108633:20;108657:52;108698:10;108657:40;:52::i;:::-;108572:137;;;;;;;;108730:32;108743:4;108749:12;108730;:32::i;:::-;108719:43;-1:-1:-1;;;;;;108846:22:0;;108842:134;;108884:45;-1:-1:-1;;;;;108884:26:0;;108911:9;108922:6;108884:26;:45::i;:::-;108951:5;108958:6;108943:22;;;;;;;;;;;108842:134;109044:34;109058:5;109065:4;109071:6;109044:13;:34::i;:::-;109100:76;109109:4;109115:14;109131:12;109145:6;109153:8;109163:12;109100:8;:76::i;:::-;109088:88;;109244:9;109257:1;109244:14;109240:126;;109274:45;-1:-1:-1;;;;;109274:26:0;;109301:9;109312:6;109274:26;:45::i;109240:126::-;109432:51;-1:-1:-1;;;;;109432:29:0;;109462:9;109473;109432:29;:51::i;:::-;107962:1528;;;;;107769:1721;;;;;;;;:::o;55351:304::-;55473:12;55491:9;-1:-1:-1;;;;;55491:14:0;55513:9;55491:36;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;55472:55;;;55605:43;55624:7;:23;;55646:1;55624:23;;;55634:9;55624:23;55605:43;;1959:25:1;;;1947:2;1932:18;55605:43:0;;;;;;;55406:249;55351:304;:::o;38027:150::-;38097:4;38120:50;38125:3;-1:-1:-1;;;;;38145:23:0;;38120:4;:50::i;56059:693::-;56225:1;-1:-1:-1;;;;;56192:35:0;:13;56206:6;56192:21;;;;;;:::i;:::-;;;;;;;;;;;;;;;-1:-1:-1;;;;;56192:21:0;:35;56188:72;;56236:24;;;;;;;;;;;;;;56188:72;56464:18;;56348:6;;44172:1;-1:-1:-1;56460:76:0;;56515:21;;;;;;;;;;;;;;56460:76;56551:9;56546:200;44172:1;56566;:24;56546:200;;;56630:13;;;;;;;;;;;;;;;;;56644:1;56630:16;;;;;;;;:::i;:::-;;;;;;;;;56612:34;;;:11;56624:1;56612:14;;;;;;;;:::i;:::-;;;;;;;:34;56608:68;;56655:21;;;;;;;;;;;;;;56608:68;56718:3;;56546:200;;54442:792;43761:5;54674:10;:28;54670:62;;;54711:21;;;;;;;;;;;;;;54670:62;54812:10;54799;:23;54795:57;;;54831:21;;;;;;;;;;;;;;54795:57;54927:6;54914:10;:19;54910:53;;;54942:21;;;;;;;;;;;;;;54910:53;54996:231;;;;;;;;55030:29;:10;:27;:29::i;:::-;54996:231;;;;;;55085:29;:10;:27;:29::i;:::-;54996:231;;;;;;55140:29;:10;:27;:29::i;:::-;54996:231;;;;;;55191:25;:6;:23;:25::i;:::-;54996:231;;;;;;;-1:-1:-1;;;;;54973:20:0;;;;;;;:13;:20;;;;;;;;;:254;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;54442:792:0:o;38345:156::-;38418:4;38441:53;38449:3;-1:-1:-1;;;;;38469:23:0;;38441:7;:53::i;30278:187::-;30351:16;30370:6;;-1:-1:-1;;;;;30386:17:0;;;;;;;;;;30418:40;;30370:6;;;;;;;30418:40;;30351:16;30418:40;30341:124;30278:187;:::o;85840:209::-;85919:7;85945:97;85981:8;85991:4;82452:18;;;;;;;;;;;;;;;;;82426:69;;;;;;;;:::i;:::-;;;;;;;;;;;;;82665:30;;;;;;85955:67;;;;;;;;;24412:66:1;24400:79;;24516:2;24512:15;;;;24529:66;24508:88;24504:1;24495:11;;24488:109;24622:2;24613:12;;24606:28;;;;24659:2;24650:12;;24643:28;24696:2;24687:12;;24114:591;85955:67:0;;;;;;;;;;;;;85945:78;;;;;;19260:3;19148:125;38828:115;38891:7;38917:19;38925:3;34443:18;;34361:107;39285:156;39359:7;39409:22;39413:3;39425:5;39409:3;:22::i;94353:115::-;93658:7;;;;93901:9;93893:38;;;;-1:-1:-1;;;93893:38:0;;12955:2:1;93893:38:0;;;12937:21:1;12994:2;12974:18;;;12967:30;13033:18;13013;;;13006:46;13069:18;;93893:38:0;12753:340:1;93893:38:0;94412:7:::1;:14:::0;;;::::1;94422:4;94412:14;::::0;;94441:20:::1;94448:12;28025:10:::0;;27946:96;94448:12:::1;94441:20;::::0;-1:-1:-1;;;;;4802:55:1;;;4784:74;;4772:2;4757:18;94441:20:0::1;;;;;;;94353:115::o:0;94600:117::-;93658:7;;;;94159:41;;;;-1:-1:-1;;;94159:41:0;;24912:2:1;94159:41:0;;;24894:21:1;24951:2;24931:18;;;24924:30;24990:22;24970:18;;;24963:50;25030:18;;94159:41:0;24710:344:1;94159:41:0;94658:7:::1;:15:::0;;;::::1;::::0;;94688:22:::1;28025:10:::0;94697:12:::1;27946:96:::0;91864:706;92283:23;92309:69;92337:4;92309:69;;;;;;;;;;;;;;;;;92317:5;-1:-1:-1;;;;;92309:27:0;;;:69;;;;;:::i;:::-;92392:17;;92283:95;;-1:-1:-1;92392:21:0;92388:176;;92487:10;92476:30;;;;;;;;;;;;:::i;:::-;92468:85;;;;-1:-1:-1;;;92468:85:0;;25261:2:1;92468:85:0;;;25243:21:1;25300:2;25280:18;;;25273:30;25339:34;25319:18;;;25312:62;25410:12;25390:18;;;25383:40;25440:19;;92468:85:0;25059:406:1;89569:241:0;89734:68;;-1:-1:-1;;;;;25751:15:1;;;89734:68:0;;;25733:34:1;25803:15;;25783:18;;;25776:43;25835:18;;;25828:34;;;89707:96:0;;89727:5;;89757:27;;25645:18:1;;89734:68:0;25470:398:1;90070:603:0;90425:10;;;90424:62;;-1:-1:-1;90441:39:0;;;;;90465:4;90441:39;;;16979:34:1;-1:-1:-1;;;;;17049:15:1;;;17029:18;;;17022:43;90441:15:0;;;;;16891:18:1;;90441:39:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:44;90424:62;90403:163;;;;-1:-1:-1;;;90403:163:0;;26075:2:1;90403:163:0;;;26057:21:1;26114:2;26094:18;;;26087:30;26153:34;26133:18;;;26126:62;26224:24;26204:18;;;26197:52;26266:19;;90403:163:0;25873:418:1;90403:163:0;90603:62;;-1:-1:-1;;;;;17908:55:1;;90603:62:0;;;17890:74:1;17980:18;;;17973:34;;;90576:90:0;;90596:5;;90626:22;;17863:18:1;;90603:62:0;17716:297:1;82981:799:0;83029:17;83250:21;82452:18;;;;;;;;;;;;;;;;;82426:69;;;;;;;;:::i;:::-;;;;;;;;;;;;;83250:43;;83603:4;83592:8;83586:15;83579:4;83569:8;83565:19;83562:1;83554:54;83541:67;-1:-1:-1;;;;;;83690:23:0;;83686:88;;83736:27;;;;;;;;;;;;;;83686:88;83048:732;82981:799;;;:::o;84213:280::-;84339:23;84435:51;84456:9;84467:6;84475:7;84484:1;84435:20;:51::i;70113:408::-;70224:20;70258:18;70290:16;70320:20;64658:6;70369:10;:17;:39;70365:76;;70417:24;;;;;;;;;;;;;;70365:76;70469:10;70458:56;;;;;;;;;;;;:::i;:::-;70451:63;;;;;;;;70113:408;;;;;:::o;111079:741::-;111384:66;;;27046:4:1;27034:17;;111384:66:0;;;;27016:36:1;;;;111384:66:0;;;;;;;;;;26989:18:1;;;;111384:66:0;;;;;;;;;111407:30;111384:66;;;111355:105;;111156:13;;;;;;-1:-1:-1;;;;;111355:15:0;;;:105;;111384:66;111355:105;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;111313:147;;;;111474:7;:34;;;;;111485:10;:17;111506:2;111485:23;111474:34;111470:344;;;111641:38;:19;111649:10;111641:19;:::i;:::-;19260:3;19148:125;111641:38;111633:46;;111470:344;;;111801:1;111785:18;;111470:344;111171:649;;111079:741;;;;:::o;109628:511::-;109863:85;;;;;27985:4:1;27973:17;;;109863:85:0;;;27955:36:1;28027:17;;28007:18;;;28000:45;28061:18;;;28054:34;;;28104:18;;;28097:34;;;28147:19;;;28140:35;;;109830:17:0;;-1:-1:-1;;;;;109863:23:0;;;;;27927:19:1;;109863:85:0;;;;;;;;;;;;;;;;;;;-1:-1:-1;109863:85:0;;;;;;;;-1:-1:-1;;109863:85:0;;;;;;;;;;;;:::i;:::-;;;109859:274;;-1:-1:-1;110121:1:0;109859:274;;;110026:10;-1:-1:-1;109859:274:0;109628:511;;;;;;;;:::o;32112:404::-;32175:4;34249:19;;;:12;;;:19;;;;;;32191:319;;-1:-1:-1;32233:23:0;;;;;;;;:11;:23;;;;;;;;;;;;;32413:18;;32391:19;;;:12;;;:19;;;;;;:40;;;;32445:11;;32191:319;-1:-1:-1;32494:5:0;32487:12;;19337:194;19401:6;19431:16;19423:24;;19419:76;;;19470:14;;;;;;;;;;;;;;19419:76;-1:-1:-1;19518:5:0;19337:194::o;19595:::-;19659:6;19689:16;19681:24;;19677:76;;;19728:14;;;;;;;;;;;;;;32684:1388;32750:4;32887:19;;;:12;;;:19;;;;;;32921:15;;32917:1149;;33290:21;33314:14;33327:1;33314:10;:14;:::i;:::-;33362:18;;33290:38;;-1:-1:-1;33342:17:0;;33362:22;;33383:1;;33362:22;:::i;:::-;33342:42;;33416:13;33403:9;:26;33399:398;;33449:17;33469:3;:11;;33481:9;33469:22;;;;;;;;:::i;:::-;;;;;;;;;33449:42;;33620:9;33591:3;:11;;33603:13;33591:26;;;;;;;;:::i;:::-;;;;;;;;;;;;:38;;;;33703:23;;;:12;;;:23;;;;;:36;;;33399:398;33875:17;;:3;;:17;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;33967:3;:12;;:19;33980:5;33967:19;;;;;;;;;;;33960:26;;;34008:4;34001:11;;;;;;;32917:1149;34050:5;34043:12;;;;;34810:118;34877:7;34903:3;:11;;34915:5;34903:18;;;;;;;;:::i;:::-;;;;;;;;;34896:25;;34810:118;;;;:::o;10362:223::-;10495:12;10526:52;10548:6;10556:4;10562:1;10565:12;10526:21;:52::i;85021:481::-;85179:23;85403:92;-1:-1:-1;;;;;85452:23:0;;85479:7;85435:52;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;85435:52:0;;;;;;;;;-1:-1:-1;;;;;85403:31:0;;;85489:5;85403:31;:92::i;:::-;85390:105;85021:481;-1:-1:-1;;;;;85021:481:0:o;11449:499::-;11614:12;11671:5;11646:21;:30;;11638:81;;;;-1:-1:-1;;;11638:81:0;;28928:2:1;11638:81:0;;;28910:21:1;28967:2;28947:18;;;28940:30;29006:34;28986:18;;;28979:62;29077:8;29057:18;;;29050:36;29103:19;;11638:81:0;28726:402:1;11638:81:0;-1:-1:-1;;;;;7966:19:0;;;11729:60;;;;-1:-1:-1;;;11729:60:0;;29335:2:1;11729:60:0;;;29317:21:1;29374:2;29354:18;;;29347:30;29413:31;29393:18;;;29386:59;29462:18;;11729:60:0;29133:353:1;11729:60:0;11801:12;11815:23;11842:6;-1:-1:-1;;;;;11842:11:0;11861:5;11868:4;11842:31;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;11800:73;;;;11890:51;11907:7;11916:10;11928:12;11890:16;:51::i;:::-;11883:58;11449:499;-1:-1:-1;;;;;;;11449:499:0:o;10947:254::-;11076:12;11107:87;11129:6;11137:4;11143:5;11107:87;;;;;;;;;;;;;;;;;:21;:87::i;14062:692::-;14208:12;14236:7;14232:516;;;-1:-1:-1;14266:10:0;14259:17;;14232:516;14377:17;;:21;14373:365;;14571:10;14565:17;14631:15;14618:10;14614:2;14610:19;14603:44;14373:365;14710:12;14703:20;;-1:-1:-1;;;14703:20:0;;;;;;;;:::i;-1:-1:-1:-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14:154:1;-1:-1:-1;;;;;93:5:1;89:54;82:5;79:65;69:93;;158:1;155;148:12;173:247;232:6;285:2;273:9;264:7;260:23;256:32;253:52;;;301:1;298;291:12;253:52;340:9;327:23;359:31;384:5;359:31;:::i;425:258::-;497:1;507:113;521:6;518:1;515:13;507:113;;;597:11;;;591:18;578:11;;;571:39;543:2;536:10;507:113;;;638:6;635:1;632:13;629:48;;;-1:-1:-1;;673:1:1;655:16;;648:27;425:258::o;688:317::-;730:3;768:5;762:12;795:6;790:3;783:19;811:63;867:6;860:4;855:3;851:14;844:4;837:5;833:16;811:63;:::i;:::-;919:2;907:15;-1:-1:-1;;903:88:1;894:98;;;;994:4;890:109;;688:317;-1:-1:-1;;688:317:1:o;1010:220::-;1159:2;1148:9;1141:21;1122:4;1179:45;1220:2;1209:9;1205:18;1197:6;1179:45;:::i;1235:118::-;1321:5;1314:13;1307:21;1300:5;1297:32;1287:60;;1343:1;1340;1333:12;1358:450;1432:6;1440;1448;1501:2;1489:9;1480:7;1476:23;1472:32;1469:52;;;1517:1;1514;1507:12;1469:52;1556:9;1543:23;1575:31;1600:5;1575:31;:::i;:::-;1625:5;-1:-1:-1;1677:2:1;1662:18;;1649:32;;-1:-1:-1;1733:2:1;1718:18;;1705:32;1746:30;1705:32;1746:30;:::i;:::-;1795:7;1785:17;;;1358:450;;;;;:::o;1995:388::-;2063:6;2071;2124:2;2112:9;2103:7;2099:23;2095:32;2092:52;;;2140:1;2137;2130:12;2092:52;2179:9;2166:23;2198:31;2223:5;2198:31;:::i;:::-;2248:5;-1:-1:-1;2305:2:1;2290:18;;2277:32;2318:33;2277:32;2318:33;:::i;:::-;2370:7;2360:17;;;1995:388;;;;;:::o;2388:121::-;2473:10;2466:5;2462:22;2455:5;2452:33;2442:61;;2499:1;2496;2489:12;2514:184;2566:77;2563:1;2556:88;2663:4;2660:1;2653:15;2687:4;2684:1;2677:15;2703:334;2774:2;2768:9;2830:2;2820:13;;-1:-1:-1;;2816:86:1;2804:99;;2933:18;2918:34;;2954:22;;;2915:62;2912:88;;;2980:18;;:::i;:::-;3016:2;3009:22;2703:334;;-1:-1:-1;2703:334:1:o;3042:245::-;3090:4;3123:18;3115:6;3112:30;3109:56;;;3145:18;;:::i;:::-;-1:-1:-1;3202:2:1;3190:15;-1:-1:-1;;3186:88:1;3276:4;3182:99;;3042:245::o;3292:462::-;3334:5;3387:3;3380:4;3372:6;3368:17;3364:27;3354:55;;3405:1;3402;3395:12;3354:55;3441:6;3428:20;3472:48;3488:31;3516:2;3488:31;:::i;:::-;3472:48;:::i;:::-;3545:2;3536:7;3529:19;3591:3;3584:4;3579:2;3571:6;3567:15;3563:26;3560:35;3557:55;;;3608:1;3605;3598:12;3557:55;3673:2;3666:4;3658:6;3654:17;3647:4;3638:7;3634:18;3621:55;3721:1;3696:16;;;3714:4;3692:27;3685:38;;;;3700:7;3292:462;-1:-1:-1;;;3292:462:1:o;3759:874::-;3871:6;3879;3887;3895;3903;3911;3964:3;3952:9;3943:7;3939:23;3935:33;3932:53;;;3981:1;3978;3971:12;3932:53;4020:9;4007:23;4039:31;4064:5;4039:31;:::i;:::-;4089:5;-1:-1:-1;4141:2:1;4126:18;;4113:32;;-1:-1:-1;4197:2:1;4182:18;;4169:32;4210:33;4169:32;4210:33;:::i;:::-;4262:7;-1:-1:-1;4316:2:1;4301:18;;4288:32;;-1:-1:-1;4372:3:1;4357:19;;4344:33;4386:32;4344:33;4386:32;:::i;:::-;4437:7;-1:-1:-1;4495:3:1;4480:19;;4467:33;4523:18;4512:30;;4509:50;;;4555:1;4552;4545:12;4509:50;4578:49;4619:7;4610:6;4599:9;4595:22;4578:49;:::i;:::-;4568:59;;;3759:874;;;;;;;;:::o;5123:347::-;5174:8;5184:6;5238:3;5231:4;5223:6;5219:17;5215:27;5205:55;;5256:1;5253;5246:12;5205:55;-1:-1:-1;5279:20:1;;5322:18;5311:30;;5308:50;;;5354:1;5351;5344:12;5308:50;5391:4;5383:6;5379:17;5367:29;;5443:3;5436:4;5427:6;5419;5415:19;5411:30;5408:39;5405:59;;;5460:1;5457;5450:12;5475:1049;5591:6;5599;5607;5615;5623;5631;5684:3;5672:9;5663:7;5659:23;5655:33;5652:53;;;5701:1;5698;5691:12;5652:53;5741:9;5728:23;5770:18;5811:2;5803:6;5800:14;5797:34;;;5827:1;5824;5817:12;5797:34;5866:58;5916:7;5907:6;5896:9;5892:22;5866:58;:::i;:::-;5943:8;;-1:-1:-1;5840:84:1;-1:-1:-1;6031:2:1;6016:18;;6003:32;;-1:-1:-1;6047:16:1;;;6044:36;;;6076:1;6073;6066:12;6044:36;6115:60;6167:7;6156:8;6145:9;6141:24;6115:60;:::i;:::-;6194:8;;-1:-1:-1;6089:86:1;-1:-1:-1;6279:2:1;6264:18;;6251:32;;-1:-1:-1;6292:30:1;6251:32;6292:30;:::i;:::-;6341:5;;-1:-1:-1;6399:2:1;6384:18;;6371:32;;6415:16;;;6412:36;;;6444:1;6441;6434:12;6412:36;;6467:51;6510:7;6499:8;6488:9;6484:24;6467:51;:::i;6529:731::-;6643:6;6651;6659;6667;6675;6683;6736:3;6724:9;6715:7;6711:23;6707:33;6704:53;;;6753:1;6750;6743:12;6704:53;6793:9;6780:23;6826:18;6818:6;6815:30;6812:50;;;6858:1;6855;6848:12;6812:50;6881:49;6922:7;6913:6;6902:9;6898:22;6881:49;:::i;:::-;6871:59;;;6980:2;6969:9;6965:18;6952:32;6993:31;7018:5;6993:31;:::i;:::-;6529:731;;7043:5;;-1:-1:-1;;;;7095:2:1;7080:18;;7067:32;;7146:2;7131:18;;7118:32;;7197:3;7182:19;;7169:33;;-1:-1:-1;7249:3:1;7234:19;;;7221:33;;-1:-1:-1;6529:731:1:o;7265:521::-;7360:6;7368;7376;7384;7392;7445:3;7433:9;7424:7;7420:23;7416:33;7413:53;;;7462:1;7459;7452:12;7413:53;7501:9;7488:23;7520:31;7545:5;7520:31;:::i;:::-;7570:5;7622:2;7607:18;;7594:32;;-1:-1:-1;7673:2:1;7658:18;;7645:32;;7724:2;7709:18;;7696:32;;-1:-1:-1;7775:3:1;7760:19;7747:33;;-1:-1:-1;7265:521:1;-1:-1:-1;;;7265:521:1:o;7983:180::-;8042:6;8095:2;8083:9;8074:7;8070:23;8066:32;8063:52;;;8111:1;8108;8101:12;8063:52;-1:-1:-1;8134:23:1;;7983:180;-1:-1:-1;7983:180:1:o;8809:1170::-;9003:4;9032:2;9072;9061:9;9057:18;9102:2;9091:9;9084:21;9125:6;9160;9154:13;9191:6;9183;9176:22;9217:2;9207:12;;9250:2;9239:9;9235:18;9228:25;;9312:2;9302:6;9299:1;9295:14;9284:9;9280:30;9276:39;9350:2;9342:6;9338:15;9371:1;9381:569;9395:6;9392:1;9389:13;9381:569;;;9484:66;9472:9;9464:6;9460:22;9456:95;9451:3;9444:108;9581:6;9575:13;9627:2;9621:9;9658:2;9650:6;9643:18;9688:48;9732:2;9724:6;9720:15;9706:12;9688:48;:::i;:::-;9783:11;;;9777:18;-1:-1:-1;;;;;9773:67:1;9756:15;;;9749:92;;;;9928:12;;;;9674:62;-1:-1:-1;9893:15:1;;;;9417:1;9410:9;9381:569;;;-1:-1:-1;9967:6:1;;8809:1170;-1:-1:-1;;;;;;;;8809:1170:1:o;9984:321::-;10053:6;10106:2;10094:9;10085:7;10081:23;10077:32;10074:52;;;10122:1;10119;10112:12;10074:52;10162:9;10149:23;10195:18;10187:6;10184:30;10181:50;;;10227:1;10224;10217:12;10181:50;10250:49;10291:7;10282:6;10271:9;10267:22;10250:49;:::i;11100:454::-;11176:6;11184;11192;11245:2;11233:9;11224:7;11220:23;11216:32;11213:52;;;11261:1;11258;11251:12;11213:52;11297:9;11284:23;11274:33;;11357:2;11346:9;11342:18;11329:32;11370:30;11394:5;11370:30;:::i;:::-;11419:5;-1:-1:-1;11476:2:1;11461:18;;11448:32;11489:33;11448:32;11489:33;:::i;11559:386::-;11626:6;11634;11687:2;11675:9;11666:7;11662:23;11658:32;11655:52;;;11703:1;11700;11693:12;11655:52;11742:9;11729:23;11761:30;11785:5;11761:30;:::i;11950:437::-;12029:1;12025:12;;;;12072;;;12093:61;;12147:4;12139:6;12135:17;12125:27;;12093:61;12200:2;12192:6;12189:14;12169:18;12166:38;12163:218;;12237:77;12234:1;12227:88;12338:4;12335:1;12328:15;12366:4;12363:1;12356:15;13098:175;13176:13;;13229:18;13218:30;;13208:41;;13198:69;;13263:1;13260;13253:12;13198:69;13098:175;;;:::o;13278:206::-;13347:6;13400:2;13388:9;13379:7;13375:23;13371:32;13368:52;;;13416:1;13413;13406:12;13368:52;13439:39;13468:9;13439:39;:::i;14021:736::-;14332:6;14321:9;14314:25;14387:18;14379:6;14375:31;14370:2;14359:9;14355:18;14348:59;-1:-1:-1;;;;;14447:6:1;14443:55;14438:2;14427:9;14423:18;14416:83;14535:6;14530:2;14519:9;14515:18;14508:34;14591:10;14583:6;14579:23;14573:3;14562:9;14558:19;14551:52;14640:3;14634;14623:9;14619:19;14612:32;14295:4;14661:46;14702:3;14691:9;14687:19;14679:6;14661:46;:::i;:::-;14653:54;;14744:6;14738:3;14727:9;14723:19;14716:35;14021:736;;;;;;;;;;:::o;15606:276::-;15737:3;15775:6;15769:13;15791:53;15837:6;15832:3;15825:4;15817:6;15813:17;15791:53;:::i;:::-;15860:16;;;;;15606:276;-1:-1:-1;;15606:276:1:o;15887:184::-;15939:77;15936:1;15929:88;16036:4;16033:1;16026:15;16060:4;16057:1;16050:15;16076:274;16116:1;16142;16132:189;;16177:77;16174:1;16167:88;16278:4;16275:1;16268:15;16306:4;16303:1;16296:15;16132:189;-1:-1:-1;16335:9:1;;16076:274::o;16355:184::-;16407:77;16404:1;16397:88;16504:4;16501:1;16494:15;16528:4;16525:1;16518:15;16544:195;16583:3;-1:-1:-1;;16607:5:1;16604:77;16601:103;;16684:18;;:::i;:::-;-1:-1:-1;16731:1:1;16720:13;;16544:195::o;17483:228::-;17523:7;17649:1;-1:-1:-1;;17577:74:1;17574:1;17571:81;17566:1;17559:9;17552:17;17548:105;17545:131;;;17656:18;;:::i;:::-;-1:-1:-1;17696:9:1;;17483:228::o;18018:184::-;18088:6;18141:2;18129:9;18120:7;18116:23;18112:32;18109:52;;;18157:1;18154;18147:12;18109:52;-1:-1:-1;18180:16:1;;18018:184;-1:-1:-1;18018:184:1:o;18207:125::-;18247:4;18275:1;18272;18269:8;18266:34;;;18280:18;;:::i;:::-;-1:-1:-1;18317:9:1;;18207:125::o;18922:379::-;19115:2;19104:9;19097:21;19078:4;19141:45;19182:2;19171:9;19167:18;19159:6;19141:45;:::i;:::-;19234:9;19226:6;19222:22;19217:2;19206:9;19202:18;19195:50;19262:33;19288:6;19280;19262:33;:::i;19306:128::-;19346:3;19377:1;19373:6;19370:1;19367:13;19364:39;;;19383:18;;:::i;:::-;-1:-1:-1;19419:9:1;;19306:128::o;19439:428::-;19492:5;19545:3;19538:4;19530:6;19526:17;19522:27;19512:55;;19563:1;19560;19553:12;19512:55;19592:6;19586:13;19623:48;19639:31;19667:2;19639:31;:::i;19623:48::-;19696:2;19687:7;19680:19;19742:3;19735:4;19730:2;19722:6;19718:15;19714:26;19711:35;19708:55;;;19759:1;19756;19749:12;19708:55;19772:64;19833:2;19826:4;19817:7;19813:18;19806:4;19798:6;19794:17;19772:64;:::i;19872:558::-;19969:6;19977;20030:2;20018:9;20009:7;20005:23;20001:32;19998:52;;;20046:1;20043;20036:12;19998:52;20079:9;20073:16;20108:18;20149:2;20141:6;20138:14;20135:34;;;20165:1;20162;20155:12;20135:34;20188:60;20240:7;20231:6;20220:9;20216:22;20188:60;:::i;:::-;20178:70;;20294:2;20283:9;20279:18;20273:25;20257:41;;20323:2;20313:8;20310:16;20307:36;;;20339:1;20336;20329:12;20307:36;;20362:62;20416:7;20405:8;20394:9;20390:24;20362:62;:::i;:::-;20352:72;;;19872:558;;;;;:::o;20435:679::-;20555:6;20563;20571;20579;20587;20640:3;20628:9;20619:7;20615:23;20611:33;20608:53;;;20657:1;20654;20647:12;20608:53;20689:9;20683:16;20708:30;20732:5;20708:30;:::i;:::-;20757:5;-1:-1:-1;20781:48:1;20825:2;20810:18;;20781:48;:::i;:::-;20771:58;;20874:2;20863:9;20859:18;20853:25;20887:33;20912:7;20887:33;:::i;:::-;20986:2;20971:18;;20965:25;21035:3;21020:19;;21014:26;20939:7;;-1:-1:-1;20965:25:1;-1:-1:-1;21049:33:1;21014:26;21049:33;:::i;:::-;21101:7;21091:17;;;20435:679;;;;;;;;:::o;21119:325::-;21207:6;21202:3;21195:19;21259:6;21252:5;21245:4;21240:3;21236:14;21223:43;;21311:1;21304:4;21295:6;21290:3;21286:16;21282:27;21275:38;21177:3;21433:4;-1:-1:-1;;21358:2:1;21350:6;21346:15;21342:88;21337:3;21333:98;21329:109;21322:116;;21119:325;;;;:::o;21449:431::-;21662:2;21651:9;21644:21;21625:4;21688:61;21745:2;21734:9;21730:18;21722:6;21714;21688:61;:::i;:::-;21797:9;21789:6;21785:22;21780:2;21769:9;21765:18;21758:50;21825:49;21867:6;21859;21851;21825:49;:::i;21885:245::-;21952:6;22005:2;21993:9;21984:7;21980:23;21976:32;21973:52;;;22021:1;22018;22011:12;21973:52;22053:9;22047:16;22072:28;22094:5;22072:28;:::i;22135:251::-;22205:6;22258:2;22246:9;22237:7;22233:23;22229:32;22226:52;;;22274:1;22271;22264:12;22226:52;22306:9;22300:16;22325:31;22350:5;22325:31;:::i;23413:696::-;23774:66;23769:3;23762:79;23744:3;23870:6;23864:13;23886:61;23940:6;23936:1;23931:3;23927:11;23920:4;23912:6;23908:17;23886:61;:::i;:::-;24010:66;24006:1;23966:16;;;;23998:10;;;23991:86;-1:-1:-1;24101:1:1;24093:10;;23413:696;-1:-1:-1;23413:696:1:o;26296:160::-;26373:13;;26426:4;26415:16;;26405:27;;26395:55;;26446:1;26443;26436:12;26461:408;26554:6;26562;26570;26578;26631:3;26619:9;26610:7;26606:23;26602:33;26599:53;;;26648:1;26645;26638:12;26599:53;26671:38;26699:9;26671:38;:::i;:::-;26661:48;;26728:47;26771:2;26760:9;26756:18;26728:47;:::i;:::-;26815:2;26800:18;;26794:25;26859:2;26844:18;;;26838:25;26461:408;;26718:57;;-1:-1:-1;26461:408:1;-1:-1:-1;;;26461:408:1:o;27342:357::-;27460:12;;27507:4;27496:16;;;27490:23;;27460:12;27525:16;;27522:171;;;-1:-1:-1;;27599:4:1;27595:17;;;;27592:1;27588:25;27584:98;27573:110;;27342:357;-1:-1:-1;27342:357:1:o;28186:184::-;28238:77;28235:1;28228:88;28335:4;28332:1;28325:15;28359:4;28356:1;28349:15;28375:346;28562:6;28557:3;28550:19;28532:3;28598:6;28592:13;28614:60;28667:6;28662:2;28657:3;28653:12;28648:2;28640:6;28636:15;28614:60;:::i;:::-;28694:16;;;;28712:2;28690:25;;28375:346;-1:-1:-1;;;28375:346:1:o","abiDefinition":[{"inputs":[{"internalType":"contract ITokenMessenger","name":"tokenMessenger_","type":"address"},{"internalType":"address","name":"owner_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CCTPGasRescueFailed","type":"error"},{"inputs":[],"name":"CCTPIncorrectChainId","type":"error"},{"inputs":[],"name":"CCTPIncorrectConfig","type":"error"},{"inputs":[],"name":"CCTPIncorrectDomain","type":"error"},{"inputs":[],"name":"CCTPIncorrectGasAmount","type":"error"},{"inputs":[],"name":"CCTPIncorrectProtocolFee","type":"error"},{"inputs":[],"name":"CCTPInsufficientAmount","type":"error"},{"inputs":[],"name":"CCTPMessageNotReceived","type":"error"},{"inputs":[],"name":"CCTPSymbolAlreadyAdded","type":"error"},{"inputs":[],"name":"CCTPSymbolIncorrect","type":"error"},{"inputs":[],"name":"CCTPTokenAlreadyAdded","type":"error"},{"inputs":[],"name":"CCTPTokenNotFound","type":"error"},{"inputs":[],"name":"CCTPZeroAddress","type":"error"},{"inputs":[],"name":"CCTPZeroAmount","type":"error"},{"inputs":[],"name":"CastOverflow","type":"error"},{"inputs":[],"name":"ForwarderDeploymentFailed","type":"error"},{"inputs":[],"name":"IncorrectRequestLength","type":"error"},{"inputs":[],"name":"RemoteCCTPDeploymentNotSet","type":"error"},{"inputs":[],"name":"UnknownRequestVersion","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ChainGasAirdropped","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"chainGasAmount","type":"uint256"}],"name":"ChainGasAmountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"originDomain","type":"uint32"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"address","name":"mintToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"requestID","type":"bytes32"}],"name":"CircleRequestFulfilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"chainId","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint64","name":"nonce","type":"uint64"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"requestVersion","type":"uint32"},{"indexed":false,"internalType":"bytes","name":"formattedRequest","type":"bytes"},{"indexed":false,"internalType":"bytes32","name":"requestID","type":"bytes32"}],"name":"CircleRequestSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"feeCollector","type":"address"},{"indexed":false,"internalType":"uint256","name":"relayerFeeAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFeeAmount","type":"uint256"}],"name":"FeeCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"relayer","type":"address"},{"indexed":false,"internalType":"address","name":"oldFeeCollector","type":"address"},{"indexed":false,"internalType":"address","name":"newFeeCollector","type":"address"}],"name":"FeeCollectorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newProtocolFee","type":"uint256"}],"name":"ProtocolFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"accumulatedFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"symbol","type":"string"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"relayerFee","type":"uint256"},{"internalType":"uint256","name":"minBaseFee","type":"uint256"},{"internalType":"uint256","name":"minSwapFee","type":"uint256"},{"internalType":"uint256","name":"maxFee","type":"uint256"}],"name":"addToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"isSwap","type":"bool"}],"name":"calculateFeeAmount","outputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chainGasAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"circleTokenPool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"feeStructures","outputs":[{"internalType":"uint40","name":"relayerFee","type":"uint40"},{"internalType":"uint72","name":"minBaseFee","type":"uint72"},{"internalType":"uint72","name":"minSwapFee","type":"uint72"},{"internalType":"uint72","name":"maxFee","type":"uint72"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBridgeTokens","outputs":[{"components":[{"internalType":"string","name":"symbol","type":"string"},{"internalType":"address","name":"token","type":"address"}],"internalType":"struct BridgeToken[]","name":"bridgeTokens","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"remoteDomain","type":"uint32"},{"internalType":"address","name":"remoteToken","type":"address"}],"name":"getLocalToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"requestID","type":"bytes32"}],"name":"isRequestFulfilled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"localDomain","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"messageTransmitter","outputs":[{"internalType":"contract IMessageTransmitter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauseSending","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint32","name":"requestVersion","type":"uint32"},{"internalType":"bytes","name":"formattedRequest","type":"bytes"}],"name":"receiveCircleToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"relayerFeeCollectors","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"remoteDomainConfig","outputs":[{"internalType":"uint32","name":"domain","type":"uint32"},{"internalType":"address","name":"synapseCCTP","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"removeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rescueGas","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"burnToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint32","name":"requestVersion","type":"uint32"},{"internalType":"bytes","name":"swapParams","type":"bytes"}],"name":"sendCircleToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newChainGasAmount","type":"uint256"}],"name":"setChainGasAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"circleToken","type":"address"},{"internalType":"address","name":"pool","type":"address"}],"name":"setCircleTokenPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"feeCollector","type":"address"}],"name":"setFeeCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newProtocolFee","type":"uint256"}],"name":"setProtocolFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"remoteChainId","type":"uint256"},{"internalType":"uint32","name":"remoteDomain","type":"uint32"},{"internalType":"address","name":"remoteSynapseCCTP","type":"address"}],"name":"setRemoteDomainConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"relayerFee","type":"uint256"},{"internalType":"uint256","name":"minBaseFee","type":"uint256"},{"internalType":"uint256","name":"minSwapFee","type":"uint256"},{"internalType":"uint256","name":"maxFee","type":"uint256"}],"name":"setTokenFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"symbolToToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenMessenger","outputs":[{"internalType":"contract ITokenMessenger","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenToSymbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpauseSending","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"withdrawProtocolFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"withdrawRelayerFees","outputs":[],"stateMutability":"nonpayable","type":"function"}],"userDoc":{"events":{"ChainGasAirdropped(uint256)":{"notice":"Emitted when the native chain gas is airdropped to a recipient"},"ChainGasAmountUpdated(uint256)":{"notice":"Emitted when the amount of native gas airdropped to recipients is updated"},"CircleRequestFulfilled(uint32,address,address,uint256,address,uint256,bytes32)":{"notice":"Emitted when a Circle token is received with an attached action request."},"CircleRequestSent(uint256,address,uint64,address,uint256,uint32,bytes,bytes32)":{"notice":"Emitted when a Circle token is sent with an attached action request."},"FeeCollected(address,uint256,uint256)":{"notice":"Emitted when the fee for relaying a CCTP message is collected"},"FeeCollectorUpdated(address,address,address)":{"notice":"Emitted when the fee collector is updated for a relayer"},"ProtocolFeeUpdated(uint256)":{"notice":"Emitted when the protocol fee is updated"}},"kind":"user","methods":{"accumulatedFees(address,address)":{"notice":"Maps fee collector address into accumulated fees for a token (feeCollector =\u003e (token =\u003e amount))"},"addToken(string,address,uint256,uint256,uint256,uint256)":{"notice":"Adds a new token to the list of supported tokens, with the given symbol and fee structure."},"calculateFeeAmount(address,uint256,bool)":{"notice":"Calculates the fee amount for bridging a token to this chain using CCTP."},"chainGasAmount()":{"notice":"Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request"},"circleTokenPool(address)":{"notice":"Returns the whitelisted liquidity pool for a given Circle token."},"feeStructures(address)":{"notice":"Maps bridge token address into CCTP fee structure"},"getBridgeTokens()":{"notice":"Returns the list of all supported bridge tokens and their symbols."},"getLocalToken(uint32,address)":{"notice":"Get the local token associated with the given remote domain and token."},"isRequestFulfilled(bytes32)":{"notice":"Checks if the given request is already fulfilled."},"localDomain()":{"notice":"Refers to the local domain number used in CCTP messages."},"pauseSending()":{"notice":"Allows the contract owner to pause the sending of CCTP tokens. Note: this does not affect the receiving of CCTP tokens."},"protocolFee()":{"notice":"Protocol fee: percentage of the relayer fee that is collected by the Protocol"},"receiveCircleToken(bytes,bytes,uint32,bytes)":{"notice":"Receive Circle token supported by CCTP with the request for the action to take."},"relayerFeeCollectors(address)":{"notice":"Maps Relayer address into collector address for accumulated Relayer's fees"},"removeToken(address)":{"notice":"Removes a token from the list of supported tokens."},"rescueGas()":{"notice":"Allows to rescue stuck gas from the contract."},"sendCircleToken(address,uint256,address,uint256,uint32,bytes)":{"notice":"Send a Circle token supported by CCTP to a given chain with the request for the action to take on the destination chain."},"setChainGasAmount(uint256)":{"notice":"Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request."},"setCircleTokenPool(address,address)":{"notice":"Sets the liquidity pool for the given Circle token."},"setFeeCollector(address)":{"notice":"Allows the Relayer to set a fee collector for accumulated fees. - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector. - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector."},"setProtocolFee(uint256)":{"notice":"Sets a new protocol fee."},"setRemoteDomainConfig(uint256,uint32,address)":{"notice":"Sets the remote domain and deployment of SynapseCCTP for the given remote chainId."},"setTokenFee(address,uint256,uint256,uint256,uint256)":{"notice":"Updates the fee structure for a supported Circle token."},"symbolToToken(string)":{"notice":"Maps bridge token symbol into bridge token address"},"tokenMessenger()":{"notice":"Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens."},"tokenToSymbol(address)":{"notice":"Maps bridge token address into bridge token symbol"},"unpauseSending()":{"notice":"Allows the contract owner to unpause the sending of CCTP tokens. Note: this does not affect the receiving of CCTP tokens."},"withdrawProtocolFees(address)":{"notice":"Allows the owner to withdraw accumulated protocol fees."},"withdrawRelayerFees(address)":{"notice":"Allows the Relayer's fee collector to withdraw accumulated relayer fees."}},"version":1},"developerDoc":{"kind":"dev","methods":{"addToken(string,address,uint256,uint256,uint256,uint256)":{"details":"The symbol must start with \"CCTP.\"","params":{"maxFee":"Maximum fee for bridging a token to this chain","minBaseFee":"Minimum fee for bridging a token to this chain using a base request","minSwapFee":"Minimum fee for bridging a token to this chain using a swap request","relayerFee":"Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`","symbol":"Symbol of the token","token":"Address of the token"}},"calculateFeeAmount(address,uint256,bool)":{"details":"Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.","params":{"amount":"Amount of the Circle tokens to be bridged to this chain","isSwap":"Whether the request is a swap request","token":"Address of the Circle token"},"returns":{"fee":" Fee amount"}},"owner()":{"details":"Returns the address of the current owner."},"paused()":{"details":"Returns true if the contract is paused, and false otherwise."},"receiveCircleToken(bytes,bytes,uint32,bytes)":{"details":"The request is a bytes array containing information about the end recipient of the tokens, as well as an optional swap action to take on this chain.The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function, or the call will revert.","params":{"formattedRequest":"Formatted request for the action to take on this chain","message":"Message raw bytes emitted by CCTP MessageTransmitter on origin chain","requestVersion":"Version of the request format","signature":"Circle's attestation for the message obtained from Circle's API"}},"removeToken(address)":{"details":"Will revert if the token is not supported."},"renounceOwnership()":{"details":"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions anymore. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner."},"sendCircleToken(address,uint256,address,uint256,uint32,bytes)":{"details":"The request is a bytes array containing information about the end recipient of the tokens, as well as an optional swap action to take on the destination chain. `chainId` refers to value from EIP-155 (block.chainid).","params":{"amount":"Amount of tokens to burn","burnToken":"Address of Circle token to burn","chainId":"Chain ID of the destination chain","recipient":"Recipient of the tokens on destination chain","requestVersion":"Version of the request format","swapParams":"Swap parameters for the action to take on the destination chain (could be empty)"}},"setFeeCollector(address)":{"details":"Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol."},"setProtocolFee(uint256)":{"details":"The protocol fee is a percentage of the relayer fee that is collected by the Protocol.","params":{"newProtocolFee":"New protocol fee, multiplied by `FEE_DENOMINATOR`"}},"setTokenFee(address,uint256,uint256,uint256,uint256)":{"details":"Will revert if the token is not supported.","params":{"maxFee":"Maximum fee for bridging a token to this chain","minBaseFee":"Minimum fee for bridging a token to this chain using a base request","minSwapFee":"Minimum fee for bridging a token to this chain using a swap request","relayerFee":"Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`","token":"Address of the token"}},"transferOwnership(address)":{"details":"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner."}},"stateVariables":{"circleTokenPool":{"details":"Returns address(0) if the token bridge+swap is not supported."}},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"contract ITokenMessenger\",\"name\":\"tokenMessenger_\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"owner_\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"CCTPGasRescueFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPIncorrectChainId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPIncorrectConfig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPIncorrectDomain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPIncorrectGasAmount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPIncorrectProtocolFee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPInsufficientAmount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPMessageNotReceived\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPSymbolAlreadyAdded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPSymbolIncorrect\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPTokenAlreadyAdded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPTokenNotFound\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPZeroAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPZeroAmount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CastOverflow\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForwarderDeploymentFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectRequestLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RemoteCCTPDeploymentNotSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnknownRequestVersion\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ChainGasAirdropped\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"chainGasAmount\",\"type\":\"uint256\"}],\"name\":\"ChainGasAmountUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"originDomain\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"mintToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"requestID\",\"type\":\"bytes32\"}],\"name\":\"CircleRequestFulfilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"requestVersion\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"formattedRequest\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"requestID\",\"type\":\"bytes32\"}],\"name\":\"CircleRequestSent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"feeCollector\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"relayerFeeAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"protocolFeeAmount\",\"type\":\"uint256\"}],\"name\":\"FeeCollected\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"relayer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldFeeCollector\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newFeeCollector\",\"type\":\"address\"}],\"name\":\"FeeCollectorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newProtocolFee\",\"type\":\"uint256\"}],\"name\":\"ProtocolFeeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"accumulatedFees\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"symbol\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"relayerFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minBaseFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minSwapFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFee\",\"type\":\"uint256\"}],\"name\":\"addToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isSwap\",\"type\":\"bool\"}],\"name\":\"calculateFeeAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"chainGasAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"circleTokenPool\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"feeStructures\",\"outputs\":[{\"internalType\":\"uint40\",\"name\":\"relayerFee\",\"type\":\"uint40\"},{\"internalType\":\"uint72\",\"name\":\"minBaseFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"minSwapFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"maxFee\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBridgeTokens\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"symbol\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"internalType\":\"struct BridgeToken[]\",\"name\":\"bridgeTokens\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"remoteDomain\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"remoteToken\",\"type\":\"address\"}],\"name\":\"getLocalToken\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestID\",\"type\":\"bytes32\"}],\"name\":\"isRequestFulfilled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"localDomain\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"messageTransmitter\",\"outputs\":[{\"internalType\":\"contract IMessageTransmitter\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pauseSending\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"protocolFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"},{\"internalType\":\"uint32\",\"name\":\"requestVersion\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"formattedRequest\",\"type\":\"bytes\"}],\"name\":\"receiveCircleToken\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"relayerFeeCollectors\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"remoteDomainConfig\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"domain\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"synapseCCTP\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"removeToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rescueGas\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"burnToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"requestVersion\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"swapParams\",\"type\":\"bytes\"}],\"name\":\"sendCircleToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newChainGasAmount\",\"type\":\"uint256\"}],\"name\":\"setChainGasAmount\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"circleToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"pool\",\"type\":\"address\"}],\"name\":\"setCircleTokenPool\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"feeCollector\",\"type\":\"address\"}],\"name\":\"setFeeCollector\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newProtocolFee\",\"type\":\"uint256\"}],\"name\":\"setProtocolFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"remoteChainId\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"remoteDomain\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"remoteSynapseCCTP\",\"type\":\"address\"}],\"name\":\"setRemoteDomainConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"relayerFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minBaseFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minSwapFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFee\",\"type\":\"uint256\"}],\"name\":\"setTokenFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"name\":\"symbolToToken\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"tokenMessenger\",\"outputs\":[{\"internalType\":\"contract ITokenMessenger\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"tokenToSymbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpauseSending\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"withdrawProtocolFees\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"withdrawRelayerFees\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"addToken(string,address,uint256,uint256,uint256,uint256)\":{\"details\":\"The symbol must start with \\\"CCTP.\\\"\",\"params\":{\"maxFee\":\"Maximum fee for bridging a token to this chain\",\"minBaseFee\":\"Minimum fee for bridging a token to this chain using a base request\",\"minSwapFee\":\"Minimum fee for bridging a token to this chain using a swap request\",\"relayerFee\":\"Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\",\"symbol\":\"Symbol of the token\",\"token\":\"Address of the token\"}},\"calculateFeeAmount(address,uint256,bool)\":{\"details\":\"Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\",\"params\":{\"amount\":\"Amount of the Circle tokens to be bridged to this chain\",\"isSwap\":\"Whether the request is a swap request\",\"token\":\"Address of the Circle token\"},\"returns\":{\"fee\":\" Fee amount\"}},\"owner()\":{\"details\":\"Returns the address of the current owner.\"},\"paused()\":{\"details\":\"Returns true if the contract is paused, and false otherwise.\"},\"receiveCircleToken(bytes,bytes,uint32,bytes)\":{\"details\":\"The request is a bytes array containing information about the end recipient of the tokens, as well as an optional swap action to take on this chain.The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function, or the call will revert.\",\"params\":{\"formattedRequest\":\"Formatted request for the action to take on this chain\",\"message\":\"Message raw bytes emitted by CCTP MessageTransmitter on origin chain\",\"requestVersion\":\"Version of the request format\",\"signature\":\"Circle's attestation for the message obtained from Circle's API\"}},\"removeToken(address)\":{\"details\":\"Will revert if the token is not supported.\"},\"renounceOwnership()\":{\"details\":\"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions anymore. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner.\"},\"sendCircleToken(address,uint256,address,uint256,uint32,bytes)\":{\"details\":\"The request is a bytes array containing information about the end recipient of the tokens, as well as an optional swap action to take on the destination chain. `chainId` refers to value from EIP-155 (block.chainid).\",\"params\":{\"amount\":\"Amount of tokens to burn\",\"burnToken\":\"Address of Circle token to burn\",\"chainId\":\"Chain ID of the destination chain\",\"recipient\":\"Recipient of the tokens on destination chain\",\"requestVersion\":\"Version of the request format\",\"swapParams\":\"Swap parameters for the action to take on the destination chain (could be empty)\"}},\"setFeeCollector(address)\":{\"details\":\"Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\"},\"setProtocolFee(uint256)\":{\"details\":\"The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\",\"params\":{\"newProtocolFee\":\"New protocol fee, multiplied by `FEE_DENOMINATOR`\"}},\"setTokenFee(address,uint256,uint256,uint256,uint256)\":{\"details\":\"Will revert if the token is not supported.\",\"params\":{\"maxFee\":\"Maximum fee for bridging a token to this chain\",\"minBaseFee\":\"Minimum fee for bridging a token to this chain using a base request\",\"minSwapFee\":\"Minimum fee for bridging a token to this chain using a swap request\",\"relayerFee\":\"Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\",\"token\":\"Address of the token\"}},\"transferOwnership(address)\":{\"details\":\"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner.\"}},\"stateVariables\":{\"circleTokenPool\":{\"details\":\"Returns address(0) if the token bridge+swap is not supported.\"}},\"version\":1},\"userdoc\":{\"events\":{\"ChainGasAirdropped(uint256)\":{\"notice\":\"Emitted when the native chain gas is airdropped to a recipient\"},\"ChainGasAmountUpdated(uint256)\":{\"notice\":\"Emitted when the amount of native gas airdropped to recipients is updated\"},\"CircleRequestFulfilled(uint32,address,address,uint256,address,uint256,bytes32)\":{\"notice\":\"Emitted when a Circle token is received with an attached action request.\"},\"CircleRequestSent(uint256,address,uint64,address,uint256,uint32,bytes,bytes32)\":{\"notice\":\"Emitted when a Circle token is sent with an attached action request.\"},\"FeeCollected(address,uint256,uint256)\":{\"notice\":\"Emitted when the fee for relaying a CCTP message is collected\"},\"FeeCollectorUpdated(address,address,address)\":{\"notice\":\"Emitted when the fee collector is updated for a relayer\"},\"ProtocolFeeUpdated(uint256)\":{\"notice\":\"Emitted when the protocol fee is updated\"}},\"kind\":\"user\",\"methods\":{\"accumulatedFees(address,address)\":{\"notice\":\"Maps fee collector address into accumulated fees for a token (feeCollector =\u003e (token =\u003e amount))\"},\"addToken(string,address,uint256,uint256,uint256,uint256)\":{\"notice\":\"Adds a new token to the list of supported tokens, with the given symbol and fee structure.\"},\"calculateFeeAmount(address,uint256,bool)\":{\"notice\":\"Calculates the fee amount for bridging a token to this chain using CCTP.\"},\"chainGasAmount()\":{\"notice\":\"Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\"},\"circleTokenPool(address)\":{\"notice\":\"Returns the whitelisted liquidity pool for a given Circle token.\"},\"feeStructures(address)\":{\"notice\":\"Maps bridge token address into CCTP fee structure\"},\"getBridgeTokens()\":{\"notice\":\"Returns the list of all supported bridge tokens and their symbols.\"},\"getLocalToken(uint32,address)\":{\"notice\":\"Get the local token associated with the given remote domain and token.\"},\"isRequestFulfilled(bytes32)\":{\"notice\":\"Checks if the given request is already fulfilled.\"},\"localDomain()\":{\"notice\":\"Refers to the local domain number used in CCTP messages.\"},\"pauseSending()\":{\"notice\":\"Allows the contract owner to pause the sending of CCTP tokens. Note: this does not affect the receiving of CCTP tokens.\"},\"protocolFee()\":{\"notice\":\"Protocol fee: percentage of the relayer fee that is collected by the Protocol\"},\"receiveCircleToken(bytes,bytes,uint32,bytes)\":{\"notice\":\"Receive Circle token supported by CCTP with the request for the action to take.\"},\"relayerFeeCollectors(address)\":{\"notice\":\"Maps Relayer address into collector address for accumulated Relayer's fees\"},\"removeToken(address)\":{\"notice\":\"Removes a token from the list of supported tokens.\"},\"rescueGas()\":{\"notice\":\"Allows to rescue stuck gas from the contract.\"},\"sendCircleToken(address,uint256,address,uint256,uint32,bytes)\":{\"notice\":\"Send a Circle token supported by CCTP to a given chain with the request for the action to take on the destination chain.\"},\"setChainGasAmount(uint256)\":{\"notice\":\"Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\"},\"setCircleTokenPool(address,address)\":{\"notice\":\"Sets the liquidity pool for the given Circle token.\"},\"setFeeCollector(address)\":{\"notice\":\"Allows the Relayer to set a fee collector for accumulated fees. - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector. - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\"},\"setProtocolFee(uint256)\":{\"notice\":\"Sets a new protocol fee.\"},\"setRemoteDomainConfig(uint256,uint32,address)\":{\"notice\":\"Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\"},\"setTokenFee(address,uint256,uint256,uint256,uint256)\":{\"notice\":\"Updates the fee structure for a supported Circle token.\"},\"symbolToToken(string)\":{\"notice\":\"Maps bridge token symbol into bridge token address\"},\"tokenMessenger()\":{\"notice\":\"Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\"},\"tokenToSymbol(address)\":{\"notice\":\"Maps bridge token address into bridge token symbol\"},\"unpauseSending()\":{\"notice\":\"Allows the contract owner to unpause the sending of CCTP tokens. Note: this does not affect the receiving of CCTP tokens.\"},\"withdrawProtocolFees(address)\":{\"notice\":\"Allows the owner to withdraw accumulated protocol fees.\"},\"withdrawRelayerFees(address)\":{\"notice\":\"Allows the Relayer's fee collector to withdraw accumulated relayer fees.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"SynapseCCTP\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{"accumulatedFees(address,address)":"d4a67c6d","addToken(string,address,uint256,uint256,uint256,uint256)":"4a85178d","calculateFeeAmount(address,uint256,bool)":"0d25aafe","chainGasAmount()":"e00a83e0","circleTokenPool(address)":"a4b1d034","feeStructures(address)":"dc72495b","getBridgeTokens()":"9c1d060e","getLocalToken(uint32,address)":"f879a41a","isRequestFulfilled(bytes32)":"92a442ea","localDomain()":"8d3638f4","messageTransmitter()":"7b04c181","owner()":"8da5cb5b","pauseSending()":"d77938e4","paused()":"5c975abb","protocolFee()":"b0e21e8a","receiveCircleToken(bytes,bytes,uint32,bytes)":"4a5ae51d","relayerFeeCollectors(address)":"41f355ee","remoteDomainConfig(uint256)":"e9259ab9","removeToken(address)":"5fa7b584","renounceOwnership()":"715018a6","rescueGas()":"40432d51","sendCircleToken(address,uint256,address,uint256,uint32,bytes)":"304ddb4c","setChainGasAmount(uint256)":"b250fe6b","setCircleTokenPool(address,address)":"2cc9e7e5","setFeeCollector(address)":"a42dce80","setProtocolFee(uint256)":"787dce3d","setRemoteDomainConfig(uint256,uint32,address)":"e9bbb36d","setTokenFee(address,uint256,uint256,uint256,uint256)":"4bdb4eed","symbolToToken(string)":"a5bc29c2","tokenMessenger()":"46117830","tokenToSymbol(address)":"0ba36121","transferOwnership(address)":"f2fde38b","unpauseSending()":"e7a64a80","withdrawProtocolFees(address)":"2d80caa5","withdrawRelayerFees(address)":"f7265b3a"}},"solidity/SynapseCCTPV1_flat.sol:SynapseCCTPEvents":{"code":"0x","runtime-code":"0x","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"","srcMapRuntime":"","abiDefinition":[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"originDomain","type":"uint32"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"address","name":"mintToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"requestID","type":"bytes32"}],"name":"CircleRequestFulfilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"chainId","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint64","name":"nonce","type":"uint64"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"requestVersion","type":"uint32"},{"indexed":false,"internalType":"bytes","name":"formattedRequest","type":"bytes"},{"indexed":false,"internalType":"bytes32","name":"requestID","type":"bytes32"}],"name":"CircleRequestSent","type":"event"}],"userDoc":{"events":{"CircleRequestFulfilled(uint32,address,address,uint256,address,uint256,bytes32)":{"notice":"Emitted when a Circle token is received with an attached action request."},"CircleRequestSent(uint256,address,uint64,address,uint256,uint32,bytes,bytes32)":{"notice":"Emitted when a Circle token is sent with an attached action request."}},"kind":"user","methods":{},"version":1},"developerDoc":{"events":{"CircleRequestFulfilled(uint32,address,address,uint256,address,uint256,bytes32)":{"params":{"amount":"Amount of tokens received by recipient","fee":"Fee paid for fulfilling the request, in minted tokens","mintToken":"Address of the minted Circle token","originDomain":"CCTP domain of the origin chain","recipient":"End recipient of the tokens on this chain","requestID":"Unique identifier of the request","token":"Address of token that recipient received"}},"CircleRequestSent(uint256,address,uint64,address,uint256,uint32,bytes,bytes32)":{"details":"To fulfill the request, the validator needs to fetch `message` from `MessageSent` event emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API. This data will need to be presented to SynapseCCTP on the destination chain, along with `requestVersion` and `formattedRequest` emitted in this event.","params":{"amount":"Amount of Circle tokens burnt","chainId":"Chain ID of the destination chain","formattedRequest":"Formatted request for the action to take on the destination chain","nonce":"Nonce of the CCTP message on origin chain","requestID":"Unique identifier of the request","requestVersion":"Version of the request format","sender":"Sender of the CCTP tokens on origin chain","token":"Address of Circle token that was burnt"}}},"kind":"dev","methods":{},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"originDomain\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"mintToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"requestID\",\"type\":\"bytes32\"}],\"name\":\"CircleRequestFulfilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"requestVersion\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"formattedRequest\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"requestID\",\"type\":\"bytes32\"}],\"name\":\"CircleRequestSent\",\"type\":\"event\"}],\"devdoc\":{\"events\":{\"CircleRequestFulfilled(uint32,address,address,uint256,address,uint256,bytes32)\":{\"params\":{\"amount\":\"Amount of tokens received by recipient\",\"fee\":\"Fee paid for fulfilling the request, in minted tokens\",\"mintToken\":\"Address of the minted Circle token\",\"originDomain\":\"CCTP domain of the origin chain\",\"recipient\":\"End recipient of the tokens on this chain\",\"requestID\":\"Unique identifier of the request\",\"token\":\"Address of token that recipient received\"}},\"CircleRequestSent(uint256,address,uint64,address,uint256,uint32,bytes,bytes32)\":{\"details\":\"To fulfill the request, the validator needs to fetch `message` from `MessageSent` event emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API. This data will need to be presented to SynapseCCTP on the destination chain, along with `requestVersion` and `formattedRequest` emitted in this event.\",\"params\":{\"amount\":\"Amount of Circle tokens burnt\",\"chainId\":\"Chain ID of the destination chain\",\"formattedRequest\":\"Formatted request for the action to take on the destination chain\",\"nonce\":\"Nonce of the CCTP message on origin chain\",\"requestID\":\"Unique identifier of the request\",\"requestVersion\":\"Version of the request format\",\"sender\":\"Sender of the CCTP tokens on origin chain\",\"token\":\"Address of Circle token that was burnt\"}}},\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"events\":{\"CircleRequestFulfilled(uint32,address,address,uint256,address,uint256,bytes32)\":{\"notice\":\"Emitted when a Circle token is received with an attached action request.\"},\"CircleRequestSent(uint256,address,uint64,address,uint256,uint32,bytes,bytes32)\":{\"notice\":\"Emitted when a Circle token is sent with an attached action request.\"}},\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"SynapseCCTPEvents\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{}},"solidity/SynapseCCTPV1_flat.sol:SynapseCCTPFees":{"code":"0x","runtime-code":"0x","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"","srcMapRuntime":"","abiDefinition":[{"inputs":[],"name":"CCTPGasRescueFailed","type":"error"},{"inputs":[],"name":"CCTPIncorrectConfig","type":"error"},{"inputs":[],"name":"CCTPIncorrectProtocolFee","type":"error"},{"inputs":[],"name":"CCTPSymbolAlreadyAdded","type":"error"},{"inputs":[],"name":"CCTPSymbolIncorrect","type":"error"},{"inputs":[],"name":"CCTPTokenAlreadyAdded","type":"error"},{"inputs":[],"name":"CCTPTokenNotFound","type":"error"},{"inputs":[],"name":"CastOverflow","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ChainGasAirdropped","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"chainGasAmount","type":"uint256"}],"name":"ChainGasAmountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"feeCollector","type":"address"},{"indexed":false,"internalType":"uint256","name":"relayerFeeAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFeeAmount","type":"uint256"}],"name":"FeeCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"relayer","type":"address"},{"indexed":false,"internalType":"address","name":"oldFeeCollector","type":"address"},{"indexed":false,"internalType":"address","name":"newFeeCollector","type":"address"}],"name":"FeeCollectorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newProtocolFee","type":"uint256"}],"name":"ProtocolFeeUpdated","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"accumulatedFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"symbol","type":"string"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"relayerFee","type":"uint256"},{"internalType":"uint256","name":"minBaseFee","type":"uint256"},{"internalType":"uint256","name":"minSwapFee","type":"uint256"},{"internalType":"uint256","name":"maxFee","type":"uint256"}],"name":"addToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"isSwap","type":"bool"}],"name":"calculateFeeAmount","outputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chainGasAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"feeStructures","outputs":[{"internalType":"uint40","name":"relayerFee","type":"uint40"},{"internalType":"uint72","name":"minBaseFee","type":"uint72"},{"internalType":"uint72","name":"minSwapFee","type":"uint72"},{"internalType":"uint72","name":"maxFee","type":"uint72"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBridgeTokens","outputs":[{"components":[{"internalType":"string","name":"symbol","type":"string"},{"internalType":"address","name":"token","type":"address"}],"internalType":"struct BridgeToken[]","name":"bridgeTokens","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"relayerFeeCollectors","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"removeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rescueGas","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newChainGasAmount","type":"uint256"}],"name":"setChainGasAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"feeCollector","type":"address"}],"name":"setFeeCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newProtocolFee","type":"uint256"}],"name":"setProtocolFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"relayerFee","type":"uint256"},{"internalType":"uint256","name":"minBaseFee","type":"uint256"},{"internalType":"uint256","name":"minSwapFee","type":"uint256"},{"internalType":"uint256","name":"maxFee","type":"uint256"}],"name":"setTokenFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"symbolToToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenToSymbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}],"userDoc":{"events":{"ChainGasAirdropped(uint256)":{"notice":"Emitted when the native chain gas is airdropped to a recipient"},"ChainGasAmountUpdated(uint256)":{"notice":"Emitted when the amount of native gas airdropped to recipients is updated"},"FeeCollected(address,uint256,uint256)":{"notice":"Emitted when the fee for relaying a CCTP message is collected"},"FeeCollectorUpdated(address,address,address)":{"notice":"Emitted when the fee collector is updated for a relayer"},"ProtocolFeeUpdated(uint256)":{"notice":"Emitted when the protocol fee is updated"}},"kind":"user","methods":{"accumulatedFees(address,address)":{"notice":"Maps fee collector address into accumulated fees for a token (feeCollector =\u003e (token =\u003e amount))"},"addToken(string,address,uint256,uint256,uint256,uint256)":{"notice":"Adds a new token to the list of supported tokens, with the given symbol and fee structure."},"calculateFeeAmount(address,uint256,bool)":{"notice":"Calculates the fee amount for bridging a token to this chain using CCTP."},"chainGasAmount()":{"notice":"Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request"},"feeStructures(address)":{"notice":"Maps bridge token address into CCTP fee structure"},"getBridgeTokens()":{"notice":"Returns the list of all supported bridge tokens and their symbols."},"protocolFee()":{"notice":"Protocol fee: percentage of the relayer fee that is collected by the Protocol"},"relayerFeeCollectors(address)":{"notice":"Maps Relayer address into collector address for accumulated Relayer's fees"},"removeToken(address)":{"notice":"Removes a token from the list of supported tokens."},"rescueGas()":{"notice":"Allows to rescue stuck gas from the contract."},"setChainGasAmount(uint256)":{"notice":"Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request."},"setFeeCollector(address)":{"notice":"Allows the Relayer to set a fee collector for accumulated fees. - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector. - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector."},"setProtocolFee(uint256)":{"notice":"Sets a new protocol fee."},"setTokenFee(address,uint256,uint256,uint256,uint256)":{"notice":"Updates the fee structure for a supported Circle token."},"symbolToToken(string)":{"notice":"Maps bridge token symbol into bridge token address"},"tokenToSymbol(address)":{"notice":"Maps bridge token address into bridge token symbol"}},"version":1},"developerDoc":{"kind":"dev","methods":{"addToken(string,address,uint256,uint256,uint256,uint256)":{"details":"The symbol must start with \"CCTP.\"","params":{"maxFee":"Maximum fee for bridging a token to this chain","minBaseFee":"Minimum fee for bridging a token to this chain using a base request","minSwapFee":"Minimum fee for bridging a token to this chain using a swap request","relayerFee":"Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`","symbol":"Symbol of the token","token":"Address of the token"}},"calculateFeeAmount(address,uint256,bool)":{"details":"Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.","params":{"amount":"Amount of the Circle tokens to be bridged to this chain","isSwap":"Whether the request is a swap request","token":"Address of the Circle token"},"returns":{"fee":" Fee amount"}},"owner()":{"details":"Returns the address of the current owner."},"removeToken(address)":{"details":"Will revert if the token is not supported."},"renounceOwnership()":{"details":"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions anymore. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner."},"setFeeCollector(address)":{"details":"Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol."},"setProtocolFee(uint256)":{"details":"The protocol fee is a percentage of the relayer fee that is collected by the Protocol.","params":{"newProtocolFee":"New protocol fee, multiplied by `FEE_DENOMINATOR`"}},"setTokenFee(address,uint256,uint256,uint256,uint256)":{"details":"Will revert if the token is not supported.","params":{"maxFee":"Maximum fee for bridging a token to this chain","minBaseFee":"Minimum fee for bridging a token to this chain using a base request","minSwapFee":"Minimum fee for bridging a token to this chain using a swap request","relayerFee":"Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`","token":"Address of the token"}},"transferOwnership(address)":{"details":"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner."}},"stateVariables":{"FEE_DENOMINATOR":{"details":"Denominator used to calculate the bridge fee"},"MAX_PROTOCOL_FEE":{"details":"Maximum protocol fee that can be set: 50%"},"MAX_RELAYER_FEE":{"details":"Maximum relayer fee that can be set: 10 bps"},"SYMBOL_PREFIX":{"details":"Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols"},"SYMBOL_PREFIX_LENGTH":{"details":"Length of the mandatory prefix used for CCTP token symbols"},"_bridgeTokens":{"details":"A list of all supported bridge tokens"},"accumulatedFees":{"details":"Fee collector address of address(0) indicates that fees are accumulated by the Protocol"},"protocolFee":{"details":"Protocol collects the full fee amount, if the Relayer hasn't set a fee collector"},"relayerFeeCollectors":{"details":"Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol"}},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"CCTPGasRescueFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPIncorrectConfig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPIncorrectProtocolFee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPSymbolAlreadyAdded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPSymbolIncorrect\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPTokenAlreadyAdded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CCTPTokenNotFound\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CastOverflow\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ChainGasAirdropped\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"chainGasAmount\",\"type\":\"uint256\"}],\"name\":\"ChainGasAmountUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"feeCollector\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"relayerFeeAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"protocolFeeAmount\",\"type\":\"uint256\"}],\"name\":\"FeeCollected\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"relayer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldFeeCollector\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newFeeCollector\",\"type\":\"address\"}],\"name\":\"FeeCollectorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newProtocolFee\",\"type\":\"uint256\"}],\"name\":\"ProtocolFeeUpdated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"accumulatedFees\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"symbol\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"relayerFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minBaseFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minSwapFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFee\",\"type\":\"uint256\"}],\"name\":\"addToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isSwap\",\"type\":\"bool\"}],\"name\":\"calculateFeeAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"chainGasAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"feeStructures\",\"outputs\":[{\"internalType\":\"uint40\",\"name\":\"relayerFee\",\"type\":\"uint40\"},{\"internalType\":\"uint72\",\"name\":\"minBaseFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"minSwapFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"maxFee\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBridgeTokens\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"symbol\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"internalType\":\"struct BridgeToken[]\",\"name\":\"bridgeTokens\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"protocolFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"relayerFeeCollectors\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"removeToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rescueGas\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newChainGasAmount\",\"type\":\"uint256\"}],\"name\":\"setChainGasAmount\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"feeCollector\",\"type\":\"address\"}],\"name\":\"setFeeCollector\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newProtocolFee\",\"type\":\"uint256\"}],\"name\":\"setProtocolFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"relayerFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minBaseFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minSwapFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFee\",\"type\":\"uint256\"}],\"name\":\"setTokenFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"name\":\"symbolToToken\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"tokenToSymbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"addToken(string,address,uint256,uint256,uint256,uint256)\":{\"details\":\"The symbol must start with \\\"CCTP.\\\"\",\"params\":{\"maxFee\":\"Maximum fee for bridging a token to this chain\",\"minBaseFee\":\"Minimum fee for bridging a token to this chain using a base request\",\"minSwapFee\":\"Minimum fee for bridging a token to this chain using a swap request\",\"relayerFee\":\"Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\",\"symbol\":\"Symbol of the token\",\"token\":\"Address of the token\"}},\"calculateFeeAmount(address,uint256,bool)\":{\"details\":\"Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\",\"params\":{\"amount\":\"Amount of the Circle tokens to be bridged to this chain\",\"isSwap\":\"Whether the request is a swap request\",\"token\":\"Address of the Circle token\"},\"returns\":{\"fee\":\" Fee amount\"}},\"owner()\":{\"details\":\"Returns the address of the current owner.\"},\"removeToken(address)\":{\"details\":\"Will revert if the token is not supported.\"},\"renounceOwnership()\":{\"details\":\"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions anymore. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner.\"},\"setFeeCollector(address)\":{\"details\":\"Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\"},\"setProtocolFee(uint256)\":{\"details\":\"The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\",\"params\":{\"newProtocolFee\":\"New protocol fee, multiplied by `FEE_DENOMINATOR`\"}},\"setTokenFee(address,uint256,uint256,uint256,uint256)\":{\"details\":\"Will revert if the token is not supported.\",\"params\":{\"maxFee\":\"Maximum fee for bridging a token to this chain\",\"minBaseFee\":\"Minimum fee for bridging a token to this chain using a base request\",\"minSwapFee\":\"Minimum fee for bridging a token to this chain using a swap request\",\"relayerFee\":\"Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\",\"token\":\"Address of the token\"}},\"transferOwnership(address)\":{\"details\":\"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner.\"}},\"stateVariables\":{\"FEE_DENOMINATOR\":{\"details\":\"Denominator used to calculate the bridge fee\"},\"MAX_PROTOCOL_FEE\":{\"details\":\"Maximum protocol fee that can be set: 50%\"},\"MAX_RELAYER_FEE\":{\"details\":\"Maximum relayer fee that can be set: 10 bps\"},\"SYMBOL_PREFIX\":{\"details\":\"Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\"},\"SYMBOL_PREFIX_LENGTH\":{\"details\":\"Length of the mandatory prefix used for CCTP token symbols\"},\"_bridgeTokens\":{\"details\":\"A list of all supported bridge tokens\"},\"accumulatedFees\":{\"details\":\"Fee collector address of address(0) indicates that fees are accumulated by the Protocol\"},\"protocolFee\":{\"details\":\"Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\"},\"relayerFeeCollectors\":{\"details\":\"Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\"}},\"version\":1},\"userdoc\":{\"events\":{\"ChainGasAirdropped(uint256)\":{\"notice\":\"Emitted when the native chain gas is airdropped to a recipient\"},\"ChainGasAmountUpdated(uint256)\":{\"notice\":\"Emitted when the amount of native gas airdropped to recipients is updated\"},\"FeeCollected(address,uint256,uint256)\":{\"notice\":\"Emitted when the fee for relaying a CCTP message is collected\"},\"FeeCollectorUpdated(address,address,address)\":{\"notice\":\"Emitted when the fee collector is updated for a relayer\"},\"ProtocolFeeUpdated(uint256)\":{\"notice\":\"Emitted when the protocol fee is updated\"}},\"kind\":\"user\",\"methods\":{\"accumulatedFees(address,address)\":{\"notice\":\"Maps fee collector address into accumulated fees for a token (feeCollector =\u003e (token =\u003e amount))\"},\"addToken(string,address,uint256,uint256,uint256,uint256)\":{\"notice\":\"Adds a new token to the list of supported tokens, with the given symbol and fee structure.\"},\"calculateFeeAmount(address,uint256,bool)\":{\"notice\":\"Calculates the fee amount for bridging a token to this chain using CCTP.\"},\"chainGasAmount()\":{\"notice\":\"Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\"},\"feeStructures(address)\":{\"notice\":\"Maps bridge token address into CCTP fee structure\"},\"getBridgeTokens()\":{\"notice\":\"Returns the list of all supported bridge tokens and their symbols.\"},\"protocolFee()\":{\"notice\":\"Protocol fee: percentage of the relayer fee that is collected by the Protocol\"},\"relayerFeeCollectors(address)\":{\"notice\":\"Maps Relayer address into collector address for accumulated Relayer's fees\"},\"removeToken(address)\":{\"notice\":\"Removes a token from the list of supported tokens.\"},\"rescueGas()\":{\"notice\":\"Allows to rescue stuck gas from the contract.\"},\"setChainGasAmount(uint256)\":{\"notice\":\"Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\"},\"setFeeCollector(address)\":{\"notice\":\"Allows the Relayer to set a fee collector for accumulated fees. - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector. - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\"},\"setProtocolFee(uint256)\":{\"notice\":\"Sets a new protocol fee.\"},\"setTokenFee(address,uint256,uint256,uint256,uint256)\":{\"notice\":\"Updates the fee structure for a supported Circle token.\"},\"symbolToToken(string)\":{\"notice\":\"Maps bridge token symbol into bridge token address\"},\"tokenToSymbol(address)\":{\"notice\":\"Maps bridge token address into bridge token symbol\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"SynapseCCTPFees\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{"accumulatedFees(address,address)":"d4a67c6d","addToken(string,address,uint256,uint256,uint256,uint256)":"4a85178d","calculateFeeAmount(address,uint256,bool)":"0d25aafe","chainGasAmount()":"e00a83e0","feeStructures(address)":"dc72495b","getBridgeTokens()":"9c1d060e","owner()":"8da5cb5b","protocolFee()":"b0e21e8a","relayerFeeCollectors(address)":"41f355ee","removeToken(address)":"5fa7b584","renounceOwnership()":"715018a6","rescueGas()":"40432d51","setChainGasAmount(uint256)":"b250fe6b","setFeeCollector(address)":"a42dce80","setProtocolFee(uint256)":"787dce3d","setTokenFee(address,uint256,uint256,uint256,uint256)":"4bdb4eed","symbolToToken(string)":"a5bc29c2","tokenToSymbol(address)":"0ba36121","transferOwnership(address)":"f2fde38b"}},"solidity/SynapseCCTPV1_flat.sol:SynapseCCTPFeesEvents":{"code":"0x","runtime-code":"0x","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"","srcMapRuntime":"","abiDefinition":[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ChainGasAirdropped","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"chainGasAmount","type":"uint256"}],"name":"ChainGasAmountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"feeCollector","type":"address"},{"indexed":false,"internalType":"uint256","name":"relayerFeeAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFeeAmount","type":"uint256"}],"name":"FeeCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"relayer","type":"address"},{"indexed":false,"internalType":"address","name":"oldFeeCollector","type":"address"},{"indexed":false,"internalType":"address","name":"newFeeCollector","type":"address"}],"name":"FeeCollectorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newProtocolFee","type":"uint256"}],"name":"ProtocolFeeUpdated","type":"event"}],"userDoc":{"events":{"ChainGasAirdropped(uint256)":{"notice":"Emitted when the native chain gas is airdropped to a recipient"},"ChainGasAmountUpdated(uint256)":{"notice":"Emitted when the amount of native gas airdropped to recipients is updated"},"FeeCollected(address,uint256,uint256)":{"notice":"Emitted when the fee for relaying a CCTP message is collected"},"FeeCollectorUpdated(address,address,address)":{"notice":"Emitted when the fee collector is updated for a relayer"},"ProtocolFeeUpdated(uint256)":{"notice":"Emitted when the protocol fee is updated"}},"kind":"user","methods":{},"version":1},"developerDoc":{"events":{"ChainGasAmountUpdated(uint256)":{"params":{"chainGasAmount":"The new amount of native gas airdropped to recipients"}},"FeeCollected(address,uint256,uint256)":{"details":"If fee collector address is not set, the full fee is collected for the protocol","params":{"feeCollector":"The fee collector address","protocolFeeAmount":"The amount of fees collected for the protocol","relayerFeeAmount":"The amount of fees collected for the relayer"}},"FeeCollectorUpdated(address,address,address)":{"params":{"newFeeCollector":"The new fee collector address: will be able to withdraw future fees","oldFeeCollector":"The old fee collector address: will be able to withdraw prior fees","relayer":"The relayer address"}},"ProtocolFeeUpdated(uint256)":{"params":{"newProtocolFee":"The new protocol fee"}}},"kind":"dev","methods":{},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ChainGasAirdropped\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"chainGasAmount\",\"type\":\"uint256\"}],\"name\":\"ChainGasAmountUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"feeCollector\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"relayerFeeAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"protocolFeeAmount\",\"type\":\"uint256\"}],\"name\":\"FeeCollected\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"relayer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldFeeCollector\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newFeeCollector\",\"type\":\"address\"}],\"name\":\"FeeCollectorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newProtocolFee\",\"type\":\"uint256\"}],\"name\":\"ProtocolFeeUpdated\",\"type\":\"event\"}],\"devdoc\":{\"events\":{\"ChainGasAmountUpdated(uint256)\":{\"params\":{\"chainGasAmount\":\"The new amount of native gas airdropped to recipients\"}},\"FeeCollected(address,uint256,uint256)\":{\"details\":\"If fee collector address is not set, the full fee is collected for the protocol\",\"params\":{\"feeCollector\":\"The fee collector address\",\"protocolFeeAmount\":\"The amount of fees collected for the protocol\",\"relayerFeeAmount\":\"The amount of fees collected for the relayer\"}},\"FeeCollectorUpdated(address,address,address)\":{\"params\":{\"newFeeCollector\":\"The new fee collector address: will be able to withdraw future fees\",\"oldFeeCollector\":\"The old fee collector address: will be able to withdraw prior fees\",\"relayer\":\"The relayer address\"}},\"ProtocolFeeUpdated(uint256)\":{\"params\":{\"newProtocolFee\":\"The new protocol fee\"}}},\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"events\":{\"ChainGasAirdropped(uint256)\":{\"notice\":\"Emitted when the native chain gas is airdropped to a recipient\"},\"ChainGasAmountUpdated(uint256)\":{\"notice\":\"Emitted when the amount of native gas airdropped to recipients is updated\"},\"FeeCollected(address,uint256,uint256)\":{\"notice\":\"Emitted when the fee for relaying a CCTP message is collected\"},\"FeeCollectorUpdated(address,address,address)\":{\"notice\":\"Emitted when the fee collector is updated for a relayer\"},\"ProtocolFeeUpdated(uint256)\":{\"notice\":\"Emitted when the protocol fee is updated\"}},\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"SynapseCCTPFeesEvents\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{}},"solidity/SynapseCCTPV1_flat.sol:TypeCasts":{"code":"0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea264697066735822122079566f2a471471f474a29e755c18b605c06a9cd08e01e2e737b04d311e49116164736f6c634300080d0033","runtime-code":"0x73000000000000000000000000000000000000000030146080604052600080fdfea264697066735822122079566f2a471471f474a29e755c18b605c06a9cd08e01e2e737b04d311e49116164736f6c634300080d0033","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.8.13; // \"using A for B global\" requires 0.8.13 or higher\n\n// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════\n\n/// @notice Struct representing a bridge token. Used as the return value in view functions.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param token Bridge token address\nstruct BridgeToken {\n string symbol;\n address token;\n}\n\n/// @notice Struct used by IPoolHandler to represent a token in a pool\n/// @param index Token index in the pool\n/// @param token Token address\nstruct IndexedToken {\n uint8 index;\n address token;\n}\n\n/// @notice Struct representing a token, and the available Actions for performing a swap.\n/// @param actionMask Bitmask representing what actions (see ActionLib) are available for swapping a token\n/// @param token Token address\nstruct LimitedToken {\n uint256 actionMask;\n address token;\n}\n\n/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.\n/// @param isWeth Whether the token represents Wrapped ETH.\n/// @param token Token address.\nstruct PoolToken {\n bool isWeth;\n address token;\n}\n\n/// @notice Struct representing a liquidity pool. Used as the return value in view functions.\n/// @param pool Pool address.\n/// @param lpToken Address of pool's LP token.\n/// @param tokens List of pool's tokens.\nstruct Pool {\n address pool;\n address lpToken;\n PoolToken[] tokens;\n}\n\n// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════\n\n/// @notice Struct representing a quote request for swapping a bridge token.\n/// Used in destination chain's SynapseRouter, hence the name \"Destination Request\".\n/// @dev tokenOut is passed externally.\n/// @param symbol Bridge token symbol: unique token ID consistent among all chains\n/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied\nstruct DestRequest {\n string symbol;\n uint256 amountIn;\n}\n\n/// @notice Struct representing a swap request for SynapseRouter.\n/// @dev tokenIn is supplied separately.\n/// @param routerAdapter Contract that will perform the swap for the Router. Address(0) specifies a \"no swap\" query.\n/// @param tokenOut Token address to swap to.\n/// @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.\n/// @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.\n/// @param rawParams ABI-encoded params for the swap that will be passed to `routerAdapter`.\n/// Should be DefaultParams for swaps via DefaultAdapter.\nstruct SwapQuery {\n address routerAdapter;\n address tokenOut;\n uint256 minAmountOut;\n uint256 deadline;\n bytes rawParams;\n}\n\nusing SwapQueryLib for SwapQuery global;\n\nlibrary SwapQueryLib {\n /// @notice Checks whether the router adapter was specified in the query.\n /// Query without a router adapter specifies that no action needs to be taken.\n function hasAdapter(SwapQuery memory query) internal pure returns (bool) {\n return query.routerAdapter != address(0);\n }\n\n /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,\n /// and if a path for this action was found.\n function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {\n // Fill the fields only if some path was found.\n if (query.minAmountOut == 0) return;\n // Empty params indicates no action needs to be done, thus no adapter is needed.\n query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;\n // Set default deadline to infinity. Not using the value of 0,\n // which would lead to every swap to revert by default.\n query.deadline = type(uint256).max;\n }\n}\n\n// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════\n\n/// @notice Struct representing parameters for swapping via DefaultAdapter.\n/// @param action Action that DefaultAdapter needs to perform.\n/// @param pool Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.\n/// @param tokenIndexFrom Token index to swap from. Used for swap/addLiquidity actions.\n/// @param tokenIndexTo Token index to swap to. Used for swap/removeLiquidity actions.\nstruct DefaultParams {\n Action action;\n address pool;\n uint8 tokenIndexFrom;\n uint8 tokenIndexTo;\n}\n\n/// @notice All possible actions that DefaultAdapter could perform.\nenum Action {\n Swap, // swap between two pools tokens\n AddLiquidity, // add liquidity in a form of a single pool token\n RemoveLiquidity, // remove liquidity in a form of a single pool token\n HandleEth // ETH \u003c\u003e WETH interaction\n}\n\nusing ActionLib for Action global;\n\n/// @notice Library for dealing with bit masks which describe what set of Actions is available.\nlibrary ActionLib {\n /// @notice Returns a bitmask with all possible actions set to True.\n function allActions() internal pure returns (uint256 actionMask) {\n actionMask = type(uint256).max;\n }\n\n /// @notice Returns whether the given action is set to True in the bitmask.\n function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {\n return actionMask \u0026 mask(action) != 0;\n }\n\n /// @notice Returns a bitmask with only the given action set to True.\n function mask(Action action) internal pure returns (uint256) {\n return 1 \u003c\u003c uint256(action);\n }\n\n /// @notice Returns a bitmask with only two given actions set to True.\n function mask(Action a, Action b) internal pure returns (uint256) {\n return mask(a) | mask(b);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\n\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length \u003e 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n\nerror CastOverflow();\n\nerror IncorrectRequestLength();\nerror UnknownRequestVersion();\n\nerror CCTPGasRescueFailed();\nerror CCTPIncorrectChainId();\nerror CCTPIncorrectConfig();\nerror CCTPIncorrectDomain();\nerror CCTPIncorrectGasAmount();\nerror CCTPIncorrectProtocolFee();\nerror CCTPInsufficientAmount();\nerror CCTPSymbolAlreadyAdded();\nerror CCTPSymbolIncorrect();\nerror CCTPTokenAlreadyAdded();\nerror CCTPTokenNotFound();\nerror CCTPZeroAddress();\nerror CCTPZeroAmount();\n\nerror CCTPMessageNotReceived();\nerror RemoteCCTPDeploymentNotSet();\nerror RemoteCCTPTokenNotSet();\n\nerror ForwarderDeploymentFailed();\n\n\n\ninterface ITokenMessenger {\n /**\n * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint\n * on the destination domain must be called by `destinationCaller`.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * depositForBurn() should be preferred for use cases where a specific destination caller is not required.\n * Emits a `DepositForBurn` event.\n * @dev reverts if:\n * - given destinationCaller is zero address\n * - given burnToken is not supported\n * - given destinationDomain has no TokenMessenger registered\n * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n * to this contract is less than `amount`.\n * - burn() reverts. For example, if `amount` is 0.\n * - MessageTransmitter returns false or reverts.\n * @param amount amount of tokens to burn\n * @param destinationDomain destination domain\n * @param mintRecipient address of mint recipient on destination domain\n * @param burnToken address of contract to burn deposited tokens, on local domain\n * @param destinationCaller caller on the destination domain, as bytes32\n * @return nonce unique nonce reserved by message\n */\n function depositForBurnWithCaller(\n uint256 amount,\n uint32 destinationDomain,\n bytes32 mintRecipient,\n address burnToken,\n bytes32 destinationCaller\n ) external returns (uint64 nonce);\n\n /**\n * @notice Handles an incoming message received by the local MessageTransmitter,\n * and takes the appropriate action. For a burn message, mints the\n * associated token to the requested recipient on the local domain.\n * @dev Validates the local sender is the local MessageTransmitter, and the\n * remote sender is a registered remote TokenMessenger for `remoteDomain`.\n * @param remoteDomain The domain where the message originated from.\n * @param sender The sender of the message (remote TokenMessenger).\n * @param messageBody The message body bytes.\n * @return success Bool, true if successful.\n */\n function handleReceiveMessage(\n uint32 remoteDomain,\n bytes32 sender,\n bytes calldata messageBody\n ) external returns (bool success);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Local Message Transmitter responsible for sending and receiving messages to/from remote domains\n function localMessageTransmitter() external view returns (address);\n\n // Minter responsible for minting and burning tokens on the local domain\n function localMinter() external view returns (address);\n}\n\n\n\ninterface IDefaultPool {\n function swap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx,\n uint256 minDy,\n uint256 deadline\n ) external returns (uint256 amountOut);\n\n function calculateSwap(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 dx\n ) external view returns (uint256 amountOut);\n\n function getToken(uint8 index) external view returns (address token);\n}\n\n\n\n\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n\n /// @dev Casts uint256 to uint40, reverts on overflow\n function safeCastToUint40(uint256 value) internal pure returns (uint40) {\n if (value \u003e type(uint40).max) {\n revert CastOverflow();\n }\n return uint40(value);\n }\n\n /// @dev Casts uint256 to uint72, reverts on overflow\n function safeCastToUint72(uint256 value) internal pure returns (uint72) {\n if (value \u003e type(uint72).max) {\n revert CastOverflow();\n }\n return uint72(value);\n }\n}\n\n\n\ninterface ITokenMinter {\n /**\n * @notice Mints `amount` of local tokens corresponding to the\n * given (`sourceDomain`, `burnToken`) pair, to `to` address.\n * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not\n * map to a nonzero local token address. This mapping can be queried using\n * getLocalToken().\n * @param sourceDomain Source domain where `burnToken` was burned.\n * @param burnToken Burned token address as bytes32.\n * @param to Address to receive minted tokens, corresponding to `burnToken`,\n * on this domain.\n * @param amount Amount of tokens to mint. Must be less than or equal\n * to the minterAllowance of this TokenMinter for given `_mintToken`.\n * @return mintToken token minted.\n */\n function mint(\n uint32 sourceDomain,\n bytes32 burnToken,\n address to,\n uint256 amount\n ) external returns (address mintToken);\n\n /**\n * @notice Burn tokens owned by this ITokenMinter.\n * @param burnToken burnable token.\n * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's\n * account balance of the given `_burnToken`.\n */\n function burn(address burnToken, uint256 amount) external;\n\n /**\n * @notice Get the local token associated with the given remote domain and token.\n * @param remoteDomain Remote domain\n * @param remoteToken Remote token\n * @return local token address\n */\n function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address);\n\n // local token (address) =\u003e maximum burn amounts per message\n function burnLimitsPerMessage(address token) external view returns (uint256);\n}\n\n\n\n// prettier-ignore\n\n\n\n\nabstract contract SynapseCCTPEvents {\n /// @notice Emitted when a Circle token is sent with an attached action request.\n /// @dev To fulfill the request, the validator needs to fetch `message` from `MessageSent` event\n /// emitted by Circle's MessageTransmitter in the same tx, then fetch `signature` for the message from Circle API.\n /// This data will need to be presented to SynapseCCTP on the destination chain,\n /// along with `requestVersion` and `formattedRequest` emitted in this event.\n /// @param chainId Chain ID of the destination chain\n /// @param sender Sender of the CCTP tokens on origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param token Address of Circle token that was burnt\n /// @param amount Amount of Circle tokens burnt\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on the destination chain\n /// @param requestID Unique identifier of the request\n event CircleRequestSent(\n uint256 chainId,\n address indexed sender,\n uint64 nonce,\n address token,\n uint256 amount,\n uint32 requestVersion,\n bytes formattedRequest,\n bytes32 requestID\n );\n\n /// @notice Emitted when a Circle token is received with an attached action request.\n /// @param originDomain CCTP domain of the origin chain\n /// @param recipient End recipient of the tokens on this chain\n /// @param mintToken Address of the minted Circle token\n /// @param fee Fee paid for fulfilling the request, in minted tokens\n /// @param token Address of token that recipient received\n /// @param amount Amount of tokens received by recipient\n /// @param requestID Unique identifier of the request\n event CircleRequestFulfilled(\n uint32 originDomain,\n address indexed recipient,\n address mintToken,\n uint256 fee,\n address token,\n uint256 amount,\n bytes32 requestID\n );\n}\n\n\n\n\n\n\n\nabstract contract SynapseCCTPFeesEvents {\n /// @notice Emitted when the fee collector is updated for a relayer\n /// @param relayer The relayer address\n /// @param oldFeeCollector The old fee collector address: will be able to withdraw prior fees\n /// @param newFeeCollector The new fee collector address: will be able to withdraw future fees\n event FeeCollectorUpdated(address indexed relayer, address oldFeeCollector, address newFeeCollector);\n\n /// @notice Emitted when the fee for relaying a CCTP message is collected\n /// @dev If fee collector address is not set, the full fee is collected for the protocol\n /// @param feeCollector The fee collector address\n /// @param relayerFeeAmount The amount of fees collected for the relayer\n /// @param protocolFeeAmount The amount of fees collected for the protocol\n event FeeCollected(address feeCollector, uint256 relayerFeeAmount, uint256 protocolFeeAmount);\n\n /// @notice Emitted when the amount of native gas airdropped to recipients is updated\n /// @param chainGasAmount The new amount of native gas airdropped to recipients\n event ChainGasAmountUpdated(uint256 chainGasAmount);\n\n /// @notice Emitted when the native chain gas is airdropped to a recipient\n event ChainGasAirdropped(uint256 amount);\n\n /// @notice Emitted when the protocol fee is updated\n /// @param newProtocolFee The new protocol fee\n event ProtocolFeeUpdated(uint256 newProtocolFee);\n}\n\n\n\n\n\n\ninterface ISynapseCCTPFees {\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee);\n\n /// @notice Gets the fee structure for bridging a token to this chain.\n /// @dev Will return 0 for all fields if the token is not supported.\n /// @param token Address of the Circle token\n /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @return maxFee Maximum fee for bridging a token to this chain\n function feeStructures(address token)\n external\n view\n returns (\n uint40 relayerFee,\n uint72 minBaseFee,\n uint72 minSwapFee,\n uint72 maxFee\n );\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens);\n\n /// @notice Returns the address of the CCTP token for a given symbol.\n /// @dev Will return address(0) if the token is not supported.\n function symbolToToken(string memory symbol) external view returns (address token);\n\n /// @notice Returns the symbol of a given CCTP token.\n /// @dev Will return empty string if the token is not supported.\n function tokenToSymbol(address token) external view returns (string memory symbol);\n}\n\n// prettier-ignore\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\n\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 =\u003e uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n\n\nabstract contract SynapseCCTPFees is SynapseCCTPFeesEvents, Ownable, ISynapseCCTPFees {\n using EnumerableSet for EnumerableSet.AddressSet;\n using TypeCasts for uint256;\n\n /// @notice CCTP fee structure for a supported Circle token.\n /// @dev Optimized for storage. 2**72 is 4*10**21, which is enough to represent adequate amounts\n /// for stable coins with 18 decimals. Circle tokens have 6 decimals, so this is more than enough.\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n struct CCTPFee {\n uint40 relayerFee;\n uint72 minBaseFee;\n uint72 minSwapFee;\n uint72 maxFee;\n }\n\n /// @dev Denominator used to calculate the bridge fee\n uint256 private constant FEE_DENOMINATOR = 10**10;\n /// @dev Maximum relayer fee that can be set: 10 bps\n uint256 private constant MAX_RELAYER_FEE = 10**7;\n /// @dev Maximum protocol fee that can be set: 50%\n uint256 private constant MAX_PROTOCOL_FEE = FEE_DENOMINATOR / 2;\n /// @dev Mandatory prefix used for CCTP token symbols to distinguish them from other bridge symbols\n bytes private constant SYMBOL_PREFIX = \"CCTP.\";\n /// @dev Length of the mandatory prefix used for CCTP token symbols\n uint256 private constant SYMBOL_PREFIX_LENGTH = 5;\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n /// @notice Maps bridge token address into bridge token symbol\n mapping(address =\u003e string) public tokenToSymbol;\n /// @notice Maps bridge token symbol into bridge token address\n mapping(string =\u003e address) public symbolToToken;\n /// @notice Maps bridge token address into CCTP fee structure\n mapping(address =\u003e CCTPFee) public feeStructures;\n /// @notice Maps fee collector address into accumulated fees for a token\n /// (feeCollector =\u003e (token =\u003e amount))\n /// @dev Fee collector address of address(0) indicates that fees are accumulated by the Protocol\n mapping(address =\u003e mapping(address =\u003e uint256)) public accumulatedFees;\n /// @notice Maps Relayer address into collector address for accumulated Relayer's fees\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol\n mapping(address =\u003e address) public relayerFeeCollectors;\n /// @notice Protocol fee: percentage of the relayer fee that is collected by the Protocol\n /// @dev Protocol collects the full fee amount, if the Relayer hasn't set a fee collector\n uint256 public protocolFee;\n /// @notice Amount of chain's native gas airdropped to the token recipient for every fulfilled CCTP request\n uint256 public chainGasAmount;\n /// @dev A list of all supported bridge tokens\n EnumerableSet.AddressSet internal _bridgeTokens;\n\n // ════════════════════════════════════════════════ ONLY OWNER ═════════════════════════════════════════════════════\n\n /// @notice Adds a new token to the list of supported tokens, with the given symbol and fee structure.\n /// @dev The symbol must start with \"CCTP.\"\n /// @param symbol Symbol of the token\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function addToken(\n string memory symbol,\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (token == address(0)) revert CCTPIncorrectConfig();\n // Add a new token to the list of supported tokens, and check that it hasn't been added before\n if (!_bridgeTokens.add(token)) revert CCTPTokenAlreadyAdded();\n // Check that symbol hasn't been added yet and starts with \"CCTP.\"\n _assertCanAddSymbol(symbol);\n // Add token \u003c\u003e symbol link\n tokenToSymbol[token] = symbol;\n symbolToToken[symbol] = token;\n // Set token fee\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Removes a token from the list of supported tokens.\n /// @dev Will revert if the token is not supported.\n function removeToken(address token) external onlyOwner {\n // Remove a token from the list of supported tokens, and check that it has been added before\n if (!_bridgeTokens.remove(token)) revert CCTPTokenNotFound();\n // Remove token \u003c\u003e symbol link\n string memory symbol = tokenToSymbol[token];\n delete tokenToSymbol[token];\n delete symbolToToken[symbol];\n // Remove token fee structure\n delete feeStructures[token];\n }\n\n /// @notice Allows to rescue stuck gas from the contract.\n function rescueGas() external onlyOwner {\n (bool success, ) = msg.sender.call{value: address(this).balance}(\"\");\n if (!success) revert CCTPGasRescueFailed();\n }\n\n /// @notice Sets the amount of chain gas airdropped to the token recipient for every fulfilled CCTP request.\n function setChainGasAmount(uint256 newChainGasAmount) external onlyOwner {\n chainGasAmount = newChainGasAmount;\n emit ChainGasAmountUpdated(newChainGasAmount);\n }\n\n /// @notice Updates the fee structure for a supported Circle token.\n /// @dev Will revert if the token is not supported.\n /// @param token Address of the token\n /// @param relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR`\n /// @param minBaseFee Minimum fee for bridging a token to this chain using a base request\n /// @param minSwapFee Minimum fee for bridging a token to this chain using a swap request\n /// @param maxFee Maximum fee for bridging a token to this chain\n function setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) external onlyOwner {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n _setTokenFee(token, relayerFee, minBaseFee, minSwapFee, maxFee);\n }\n\n /// @notice Sets a new protocol fee.\n /// @dev The protocol fee is a percentage of the relayer fee that is collected by the Protocol.\n /// @param newProtocolFee New protocol fee, multiplied by `FEE_DENOMINATOR`\n function setProtocolFee(uint256 newProtocolFee) external onlyOwner {\n if (newProtocolFee \u003e MAX_PROTOCOL_FEE) revert CCTPIncorrectProtocolFee();\n protocolFee = newProtocolFee;\n emit ProtocolFeeUpdated(newProtocolFee);\n }\n\n // ═══════════════════════════════════════════ RELAYER INTERACTIONS ════════════════════════════════════════════════\n\n /// @notice Allows the Relayer to set a fee collector for accumulated fees.\n /// - New fees accumulated by the Relayer could only be withdrawn by new Relayer's fee collector.\n /// - Old fees accumulated by the Relayer could only be withdrawn by old Relayer's fee collector.\n /// @dev Default value of address(0) indicates that a Relayer's fees are accumulated by the Protocol.\n function setFeeCollector(address feeCollector) external {\n address oldFeeCollector = relayerFeeCollectors[msg.sender];\n relayerFeeCollectors[msg.sender] = feeCollector;\n emit FeeCollectorUpdated(msg.sender, oldFeeCollector, feeCollector);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Calculates the fee amount for bridging a token to this chain using CCTP.\n /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n /// @param token Address of the Circle token\n /// @param amount Amount of the Circle tokens to be bridged to this chain\n /// @param isSwap Whether the request is a swap request\n /// @return fee Fee amount\n function calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) external view returns (uint256 fee) {\n return _calculateFeeAmount(token, amount, isSwap);\n }\n\n /// @notice Returns the list of all supported bridge tokens and their symbols.\n function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens) {\n uint256 length = _bridgeTokens.length();\n bridgeTokens = new BridgeToken[](length);\n for (uint256 i = 0; i \u003c length; i++) {\n address token = _bridgeTokens.at(i);\n bridgeTokens[i] = BridgeToken({symbol: tokenToSymbol[token], token: token});\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Applies the relayer fee and updates the accumulated fee amount for the token.\n /// Will revert if the fee exceeds the token amount, or token is not supported.\n function _applyRelayerFee(\n address token,\n uint256 amount,\n bool isSwap\n ) internal returns (uint256 amountAfterFee, uint256 fee) {\n if (!_bridgeTokens.contains(token)) revert CCTPTokenNotFound();\n fee = _calculateFeeAmount(token, amount, isSwap);\n if (fee \u003e= amount) revert CCTPInsufficientAmount();\n // Could use the unchecked math, as we already checked that fee \u003c amount\n unchecked {\n amountAfterFee = amount - fee;\n }\n // Check if the Relayer has specified a fee collector\n address feeCollector = relayerFeeCollectors[msg.sender];\n if (feeCollector == address(0)) {\n // If the fee collector is not set, the Protocol will collect the full fees\n accumulatedFees[address(0)][token] += fee;\n emit FeeCollected(address(0), 0, fee);\n } else {\n // Otherwise, the Relayer and the Protocol will split the fees\n uint256 protocolFeeAmount = (fee * protocolFee) / FEE_DENOMINATOR;\n uint256 relayerFeeAmount = fee - protocolFeeAmount;\n accumulatedFees[address(0)][token] += protocolFeeAmount;\n accumulatedFees[feeCollector][token] += relayerFeeAmount;\n emit FeeCollected(feeCollector, relayerFeeAmount, protocolFeeAmount);\n }\n }\n\n /// @dev Sets the fee structure for a supported Circle token.\n function _setTokenFee(\n address token,\n uint256 relayerFee,\n uint256 minBaseFee,\n uint256 minSwapFee,\n uint256 maxFee\n ) internal {\n // Check that relayer fee is not too high\n if (relayerFee \u003e MAX_RELAYER_FEE) revert CCTPIncorrectConfig();\n // Min base fee must not exceed min swap fee\n if (minBaseFee \u003e minSwapFee) revert CCTPIncorrectConfig();\n // Min swap fee must not exceed max fee\n if (minSwapFee \u003e maxFee) revert CCTPIncorrectConfig();\n feeStructures[token] = CCTPFee({\n relayerFee: relayerFee.safeCastToUint40(),\n minBaseFee: minBaseFee.safeCastToUint72(),\n minSwapFee: minSwapFee.safeCastToUint72(),\n maxFee: maxFee.safeCastToUint72()\n });\n }\n\n /// @dev Transfers `msg.value` to the recipient. Assumes that `msg.value == chainGasAmount` at this point.\n function _transferMsgValue(address recipient) internal {\n // Try to send the gas airdrop to the recipient\n (bool success, ) = recipient.call{value: msg.value}(\"\");\n // If the transfer failed, set the emitted amount to 0\n emit ChainGasAirdropped(success ? msg.value : 0);\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Checks that the symbol hasn't been added yet and starts with \"CCTP.\"\n function _assertCanAddSymbol(string memory symbol) internal view {\n // Check if the symbol has already been added\n if (symbolToToken[symbol] != address(0)) revert CCTPSymbolAlreadyAdded();\n // Cast to bytes to check the length\n bytes memory symbolBytes = bytes(symbol);\n // Check that symbol is correct: starts with \"CCTP.\" and has at least 1 more character\n if (symbolBytes.length \u003c= SYMBOL_PREFIX_LENGTH) revert CCTPSymbolIncorrect();\n for (uint256 i = 0; i \u003c SYMBOL_PREFIX_LENGTH; ) {\n if (symbolBytes[i] != SYMBOL_PREFIX[i]) revert CCTPSymbolIncorrect();\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev Calculates the fee amount for bridging a token to this chain using CCTP.\n /// Will not check if fee exceeds the token amount. Will return 0 if the token is not supported.\n function _calculateFeeAmount(\n address token,\n uint256 amount,\n bool isSwap\n ) internal view returns (uint256 fee) {\n CCTPFee memory feeStructure = feeStructures[token];\n // Calculate the fee amount\n fee = (amount * feeStructure.relayerFee) / FEE_DENOMINATOR;\n // Apply minimum fee\n uint256 minFee = isSwap ? feeStructure.minSwapFee : feeStructure.minBaseFee;\n if (fee \u003c minFee) fee = minFee;\n // Apply maximum fee\n if (fee \u003e feeStructure.maxFee) fee = feeStructure.maxFee;\n }\n}\n\ncontract MessageTransmitter {\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success){\n return true;\n }\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64){\n return 1;\n }\n function localDomain() external view returns (uint32){\n return 1;\n }\n function nextAvailableNonce() external view returns (uint64){\n return 1;\n }\n function localMessageTransmitter() external view returns (address){\n return address(this);\n }\n}\n\n\ninterface IMessageTransmitter {\n /**\n * @notice Receives an incoming message, validating the header and passing\n * the body to application-specific handler.\n * @param message The message raw bytes\n * @param signature The message signature\n * @return success bool, true if successful\n */\n function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);\n\n /**\n * @notice Sends an outgoing message from the source domain, with a specified caller on the\n * destination domain.\n * @dev Increment nonce, format the message, and emit `MessageSent` event with message information.\n * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible\n * to broadcast the message on the destination domain. This is an advanced feature, and the standard\n * sendMessage() should be preferred for use cases where a specific destination caller is not required.\n * @param destinationDomain Domain of destination chain\n * @param recipient Address of message recipient on destination domain as bytes32\n * @param destinationCaller caller on the destination domain, as bytes32\n * @param messageBody Raw bytes content of message\n * @return nonce reserved by message\n */\n function sendMessageWithCaller(\n uint32 destinationDomain,\n bytes32 recipient,\n bytes32 destinationCaller,\n bytes calldata messageBody\n ) external returns (uint64);\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n // Domain of chain on which the contract is deployed\n function localDomain() external view returns (uint32);\n\n // Next available nonce from this source domain\n function nextAvailableNonce() external view returns (uint64);\n}\n\n\n\n\n\n\ninterface ISynapseCCTP {\n /// @notice Send a Circle token supported by CCTP to a given chain\n /// with the request for the action to take on the destination chain.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on the destination chain.\n /// `chainId` refers to value from EIP-155 (block.chainid).\n /// @param recipient Recipient of the tokens on destination chain\n /// @param chainId Chain ID of the destination chain\n /// @param burnToken Address of Circle token to burn\n /// @param amount Amount of tokens to burn\n /// @param requestVersion Version of the request format\n /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty)\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external;\n\n /// @notice Receive Circle token supported by CCTP with the request for the action to take.\n /// @dev The request is a bytes array containing information about the end recipient of the tokens,\n /// as well as an optional swap action to take on this chain.\n /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function,\n /// or the call will revert.\n /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain\n /// @param signature Circle's attestation for the message obtained from Circle's API\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request for the action to take on this chain\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Returns the whitelisted liquidity pool for a given Circle token.\n /// @dev Returns address(0) if the token bridge+swap is not supported.\n function circleTokenPool(address token) external view returns (address pool);\n\n /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens.\n function tokenMessenger() external view returns (ITokenMessenger);\n}\n\n\n\n\n\n\n\n\n/// # Base Request layout\n///\n/// | Field | Type | Description |\n/// | --------------- | ------- | ---------------------------------------------- |\n/// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP |\n/// | nonce | uint64 | Nonce of the CCTP message on origin chain |\n/// | originBurnToken | address | Circle token that was burned on origin chain |\n/// | amount | uint256 | Amount of tokens burned on origin chain |\n/// | recipient | address | Recipient of the tokens on destination chain |\n///\n/// # Swap Params layout\n///\n/// | Field | Type | Description |\n/// | -------------- | ------- | ------------------------------------------------------------- |\n/// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool |\n/// | tokenIndexTo | uint8 | Index of the final token in the pool |\n/// | deadline | uint256 | Latest timestamp to execute the swap |\n/// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap |\nlibrary RequestLib {\n uint32 internal constant REQUEST_BASE = 0;\n uint32 internal constant REQUEST_SWAP = 1;\n\n /// @notice Length of the encoded base request.\n uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32;\n /// @notice Length of the encoded swap parameters.\n uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32;\n /// @notice Length of the encoded swap request.\n /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length.\n uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH;\n\n // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════\n\n /// @notice Formats the base request into a bytes array.\n /// @param originDomain Domain of the origin chain\n /// @param nonce Nonce of the CCTP message on origin chain\n /// @param originBurnToken Circle token that was burned on origin chain\n /// @param amount Amount of tokens burned on origin chain\n /// @param recipient Recipient of the tokens on destination chain\n /// @return formattedRequest Properly formatted base request\n function formatBaseRequest(\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n ) internal pure returns (bytes memory formattedRequest) {\n return abi.encode(originDomain, nonce, originBurnToken, amount, recipient);\n }\n\n /// @notice Formats the swap parameters part of the swap request into a bytes array.\n /// @param tokenIndexFrom Index of the minted Circle token in the pool\n /// @param tokenIndexTo Index of the final token in the pool\n /// @param deadline Latest timestamp to execute the swap\n /// @param minAmountOut Minimum amount of tokens to receive from the swap\n /// @return formattedSwapParams Properly formatted swap parameters\n function formatSwapParams(\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n ) internal pure returns (bytes memory formattedSwapParams) {\n return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut);\n }\n\n /// @notice Formats the request into a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Base request is not properly formatted.\n /// - Swap parameters are specified for a base request.\n /// - Swap parameters are not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param baseRequest Formatted base request\n /// @param swapParams Formatted swap parameters\n /// @return formattedRequest Properly formatted request\n function formatRequest(\n uint32 requestVersion,\n bytes memory baseRequest,\n bytes memory swapParams\n ) internal pure returns (bytes memory formattedRequest) {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n if (requestVersion == REQUEST_BASE) {\n if (swapParams.length != 0) revert IncorrectRequestLength();\n // swapParams is empty, so we can just return the base request\n return baseRequest;\n } else if (requestVersion == REQUEST_SWAP) {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n // Encode both the base request and the swap parameters\n return abi.encode(baseRequest, swapParams);\n } else {\n revert UnknownRequestVersion();\n }\n }\n\n // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════\n\n /// @notice Decodes the base request from a bytes array.\n /// @dev Will revert if the request is not properly formatted.\n /// @param baseRequest Formatted base request\n /// @return originDomain Domain of the origin chain\n /// @return nonce Nonce of the CCTP message on origin domain\n /// @return originBurnToken Circle token that was burned on origin domain\n /// @return amount Amount of tokens to burn\n /// @return recipient Recipient of the tokens on destination domain\n function decodeBaseRequest(bytes memory baseRequest)\n internal\n pure\n returns (\n uint32 originDomain,\n uint64 nonce,\n address originBurnToken,\n uint256 amount,\n address recipient\n )\n {\n if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return abi.decode(baseRequest, (uint32, uint64, address, uint256, address));\n }\n\n /// @notice Decodes the swap parameters from a bytes array.\n /// @dev Will revert if the swap parameters are not properly formatted.\n /// @param swapParams Formatted swap parameters\n /// @return tokenIndexFrom Index of the minted Circle token in the pool\n /// @return tokenIndexTo Index of the final token in the pool\n /// @return deadline Latest timestamp to execute the swap\n /// @return minAmountOut Minimum amount of tokens to receive from the swap\n function decodeSwapParams(bytes memory swapParams)\n internal\n pure\n returns (\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 deadline,\n uint256 minAmountOut\n )\n {\n if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength();\n return abi.decode(swapParams, (uint8, uint8, uint256, uint256));\n }\n\n /// @notice Decodes the versioned request from a bytes array.\n /// @dev Will revert if the either of these is true:\n /// - Request version is unknown.\n /// - Request is not properly formatted.\n /// @param requestVersion Version of the request format\n /// @param formattedRequest Formatted request\n /// @return baseRequest Formatted base request\n /// @return swapParams Formatted swap parameters\n function decodeRequest(uint32 requestVersion, bytes memory formattedRequest)\n internal\n pure\n returns (bytes memory baseRequest, bytes memory swapParams)\n {\n if (requestVersion == REQUEST_BASE) {\n if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength();\n return (formattedRequest, \"\");\n } else if (requestVersion == REQUEST_SWAP) {\n if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength();\n return abi.decode(formattedRequest, (bytes, bytes));\n } else {\n revert UnknownRequestVersion();\n }\n }\n}\n\n\n\n\n\n\n\n\n\n/// Minimal Forwarder is a EIP-1167 (Minimal Proxy Contract) spin-off that\n/// forwards all calls to a any target address with any payload.\n/// Unlike EIP-1167, delegates calls are not used, so the forwarder contract\n/// is `msg.sender` as far as the target contract is concerned.\n/// # Minimal Forwarder Bytecode\n/// Inspired by [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167).\n/// Following changes were made:\n/// - Target address is not saved in the deployed contract code, but is passed as a part of the payload.\n/// - To forward a call, the sender needs to provide the target address as the first 32 bytes of the payload.\n/// - The payload to pass to the target contract occupies the rest of the payload, having an offset of 32 bytes.\n/// - The target address is derived using CALLDATALOAD.\n/// - CALLVALUE is used to pass the msg.value to the target contract.\n/// - `call()` is used instead of `delegatecall()`.\n/// ## Bytecode Table\n/// | Pos | OP | OP + Args | Description | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |\n/// | ---- | ---- | --------- | -------------- | --- | ---- | --- | --- | ------ | --- | ------ | ------ |\n/// | 0x00 | 0x60 | 0x6020 | push1 0x20 | | | | | | | | 32 |\n/// | 0x02 | 0x36 | 0x36 | calldatasize | | | | | | | cds | 32 |\n/// | 0x03 | 0x03 | 0x03 | sub | | | | | | | | cds-32 |\n/// | 0x04 | 0x80 | 0x80 | dup1 | | | | | | | cds-32 | cds-32 |\n/// | 0x05 | 0x60 | 0x6020 | push1 0x20 | | | | | | 32 | cds-32 | cds-32 |\n/// | 0x07 | 0x3d | 0x3d | returndatasize | | | | | 0 | 32 | cds-32 | cds-32 |\n/// | 0x08 | 0x37 | 0x37 | calldatacopy | | | | | | | | cds-32 |\n/// | 0x09 | 0x3d | 0x3d | returndatasize | | | | | | | 0 | cds-32 |\n/// | 0x0a | 0x3d | 0x3d | returndatasize | | | | | | 0 | 0 | cds-32 |\n/// | 0x0b | 0x3d | 0x3d | returndatasize | | | | | 0 | 0 | 0 | cds-32 |\n/// | 0x0c | 0x92 | 0x92 | swap3 | | | | | cds-32 | 0 | 0 | 0 |\n/// | 0x0d | 0x3d | 0x3d | returndatasize | | | | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0e | 0x34 | 0x34 | callvalue | | | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x0f | 0x3d | 0x3d | returndatasize | | 0 | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x10 | 0x35 | 0x35 | calldataload | | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x11 | 0x5a | 0x5a | gas | gas | addr | val | 0 | cds-32 | 0 | 0 | 0 |\n/// | 0x12 | 0xf1 | 0xf1 | call | | | | | | | suc | 0 |\n/// | 0x13 | 0x3d | 0x3d | returndatasize | | | | | | rds | suc | 0 |\n/// | 0x14 | 0x82 | 0x82 | dup3 | | | | | 0 | rds | suc | 0 |\n/// | 0x15 | 0x80 | 0x80 | dup1 | | | | 0 | 0 | rds | suc | 0 |\n/// | 0x16 | 0x3e | 0x3e | returndatacopy | | | | | | | suc | 0 |\n/// | 0x17 | 0x90 | 0x90 | swap1 | | | | | | | 0 | suc |\n/// | 0x18 | 0x3d | 0x3d | returndatasize | | | | | | rds | 0 | suc |\n/// | 0x19 | 0x91 | 0x91 | swap2 | | | | | | suc | 0 | rds |\n/// | 0x1a | 0x60 | 0x601e | push1 0x1e | | | | | 0x1e | suc | 0 | rds |\n/// | 0x1c | 0x57 | 0x57 | jumpi | | | | | | | 0 | rds |\n/// | 0x1d | 0xfd | 0xfd | revert | | | | | | | | |\n/// | 0x1e | 0x5b | 0x5b | jumpdest | | | | | | | 0 | rds |\n/// | 0x1f | 0xf3 | 0xf3 | return | | | | | | | | |\n/// \u003e - Opcode + Args refers to the bytecode of the opcode and its arguments (if there are any).\n/// \u003e - Stack View (S7..S0) is shown after the execution of the opcode.\n/// \u003e - The stack elements are shown from top to bottom.\n/// \u003e Opcodes are typically dealing with the top stack elements, so they are shown first.\n/// \u003e - `cds` refers to the calldata size.\n/// \u003e - `rds` refers to the returndata size (which is zero before the first external call).\n/// \u003e - `val` refers to the provided `msg.value`.\n/// \u003e - `addr` refers to the target address loaded from calldata.\n/// \u003e - `gas` refers to the return value of the `gas()` opcode: the amount of gas left.\n/// \u003e - `suc` refers to the return value of the `call()` opcode: 0 on failure, 1 on success.\n/// ## Bytecode Explanation\n/// - `0x00..0x03` - Calculate the offset of the payload in the calldata (first 32 bytes is target address).\n/// \u003e - `sub` pops the top two stack items, subtracts them, and pushes the result onto the stack.\n/// - `0x04..0x04` - Duplicate the offset to use it later as \"payload length\".\n/// \u003e - `dup1` duplicates the top stack item.\n/// - `0x05..0x08` - Copy the target call payload to memory.\n/// \u003e - `calldatacopy` copies a portion of the calldata to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, calldata offset to read from, and length of the data to copy.\n/// - `0x09..0x11` - Prepare the stack for the `call` opcode.\n/// \u003e - We are putting an extra zero on the stack to use it later on, as `returndatacopy` will not return zero\n/// \u003e after we perform the first external call.\n/// \u003e - `swap3` swaps the top stack item with the fourth stack item.\n/// \u003e - `callvalue` pushes `msg.value` onto the stack.\n/// \u003e - `calldataload` pushes a word (32 bytes) onto the stack from calldata. Pops the calldata offset from the stack.\n/// \u003e Writes the word from calldata to the stack. We are using offset==0 to load the target address.\n/// \u003e - `gas` pushes the remaining gas onto the stack.\n/// - `0x12..0x12` - Call the target contract.\n/// \u003e - `call` issues an external call to a target address.\n/// \u003e - Pops seven top stack items: gas, target address, value, input offset, input length,\n/// \u003e memory offset to write return data to, and length of return data to write to memory.\n/// \u003e - Pushes on stack: 0 on failure, 1 on success.\n/// - `0x13..0x16` - Copy the return data to memory.\n/// \u003e - `returndatasize` pushes the size of the returned data from the external call onto the stack.\n/// \u003e - `dup3` duplicates the third stack item.\n/// \u003e - `returncopydata` copies a portion of the returned data to memory. Pops three top stack elements:\n/// \u003e memory offset to write to, return data offset to read from, and length of the data to copy.\n/// - `0x17..0x1b` - Prepare the stack for either revert or return: jump dst, success flag, zero, and return data size.\n/// \u003e - `swap1` swaps the top stack item with the second stack item.\n/// \u003e - `swap2` swaps the top stack item with the third stack item.\n/// \u003e - `0x1e` refers to the position of the `jumpdest` opcode.\n/// \u003e It is used to jump to the `return` opcode, if call was successful.\n/// - `0x1c..0x1c` - Jump to 0x1e position, if call was successful.\n/// \u003e - `jumpi` pops two top stack items: jump destination and jump condition.\n/// \u003e If jump condition is nonzero, jumps to the jump destination.\n/// - `0x1d..0x1d` - Revert if call was unsuccessful.\n/// \u003e - `revert` pops two top stack items: memory offset to read revert message from and length of the revert message.\n/// \u003e - This allows us to bubble the revert message from the external call.\n/// - `0x1e..0x1e` - Jump destination for successful call.\n/// \u003e - `jumpdest` is a no-op that marks a valid jump destination.\n/// - `0x1f..0x1f` - Return if call was successful.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to reuse the return data from the external call.\n/// # Minimal Forwarder Init Code\n/// Inspired by [Create3 Init Code](https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol).\n/// Following changes were made:\n/// - Adjusted bytecode length to 32 bytes.\n/// - Replaced second PUSH1 opcode with RETURNDATASIZE to push 0 onto the stack.\n/// \u003e `bytecode` refers to the bytecode specified in the above table.\n/// ## Init Code Table\n/// | Pos | OP | OP + Args | Description | S1 | S0 |\n/// | ---- | ---- | --------- | --------------- | --- | -------- |\n/// | 0x00 | 0x7f | 0x7fXXXX | push32 bytecode | | bytecode |\n/// | 0x1b | 0x3d | 0x3d | returndatasize | 0 | bytecode |\n/// | 0x1c | 0x52 | 0x52 | mstore | | |\n/// | 0x1d | 0x60 | 0x6020 | push1 0x20 | | 32 |\n/// | 0x1f | 0x3d | 0x3d | returndatasize | 0 | 32 |\n/// | 0x20 | 0xf3 | 0xf3 | return | | |\n/// \u003e Init Code is executed when a contract is deployed. The returned value is saved as the contract code.\n/// \u003e Therefore, the init code is constructed in such a way that it returns the Minimal Forwarder bytecode.\n/// ## Init Code Explanation\n/// - `0x00..0x1a` - Push the Minimal Forwarder bytecode onto the stack.\n/// \u003e - `push32` pushes 32 bytes as a single stack item onto the stack.\n/// - `0x1b..0x1b` - Push 0 onto the stack.\n/// \u003e No external calls were made, so the return data size is 0.\n/// - `0x1c..0x1c` - Write the Minimal Forwarder bytecode to memory.\n/// \u003e - `mstore` pops two top stack items: memory offset to write to and value to write.\n/// \u003e - Minimal Forwarder bytecode is 32 bytes long, so we need a single `mstore` to write it to memory.\n/// - `0x1d..0x1f` - Prepare stack for `return` opcode.\n/// \u003e - We need to put `0 32` on the stack in order to return first 32 bytes of memory.\n/// - `0x20..0x20` - Return the Minimal Forwarder bytecode.\n/// \u003e - `return` pops two top stack items: memory offset to read return data from and length of the return data.\n/// \u003e - This allows us to return the Minimal Forwarder bytecode.\nlibrary MinimalForwarderLib {\n using Address for address;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Minimal Forwarder deployed bytecode. See the above table for more details.\n bytes internal constant FORWARDER_BYTECODE =\n hex\"60_20_36_03_80_60_20_3d_37_3d_3d_3d_92_3d_34_3d_35_5a_f1_3d_82_80_3e_90_3d_91_60_1e_57_fd_5b_f3\";\n\n /// @notice Init code to deploy a minimal forwarder contract.\n bytes internal constant FORWARDER_INIT_CODE = abi.encodePacked(hex\"7f\", FORWARDER_BYTECODE, hex\"3d_52_60_20_3d_f3\");\n\n /// @notice Hash of the minimal forwarder init code. Used to predict the address of a deployed forwarder.\n bytes32 internal constant FORWARDER_INIT_CODE_HASH = keccak256(FORWARDER_INIT_CODE);\n\n /// @notice Deploys a minimal forwarder contract using `CREATE2` with a given salt.\n /// @dev Will revert if the salt is already used.\n /// @param salt The salt to use for the deployment\n /// @return forwarder The address of the deployed minimal forwarder\n function deploy(bytes32 salt) internal returns (address forwarder) {\n // `bytes arr` is stored in memory in the following way\n // 1. First, uint256 arr.length is stored. That requires 32 bytes (0x20).\n // 2. Then, the array data is stored.\n bytes memory initCode = FORWARDER_INIT_CODE;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Deploy the minimal forwarder with our pre-made bytecode via CREATE2.\n // We add 0x20 to get the location where the init code starts.\n forwarder := create2(0, add(initCode, 0x20), mload(initCode), salt)\n }\n // Deploy fails if the given salt is already used.\n if (forwarder == address(0)) {\n revert ForwarderDeploymentFailed();\n }\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @return returnData The return data from the target contract\n function forwardCall(\n address forwarder,\n address target,\n bytes memory payload\n ) internal returns (bytes memory returnData) {\n // Forward a call without any ETH value\n returnData = forwardCallWithValue(forwarder, target, payload, 0);\n }\n\n /// @notice Forwards a call to a target address using a minimal forwarder with the given `msg.value`.\n /// @dev Will bubble up any revert messages from the target.\n /// @param forwarder The address of the minimal forwarder to use\n /// @param target The address of the target contract to call\n /// @param payload The payload to pass to the target contract\n /// @param value The amount of ETH to send with the call\n /// @return returnData The return data from the target contract\n function forwardCallWithValue(\n address forwarder,\n address target,\n bytes memory payload,\n uint256 value\n ) internal returns (bytes memory returnData) {\n // The payload to pass to the forwarder:\n // 1. First 32 bytes is the encoded target address\n // 2. The rest is the encoded payload to pass to the target\n returnData = forwarder.functionCallWithValue(abi.encodePacked(target.addressToBytes32(), payload), value);\n }\n\n /// @notice Predicts the address of a minimal forwarder contract deployed using `deploy()`.\n /// @param deployer The address of the deployer of the minimal forwarder\n /// @param salt The salt to use for the deployment\n /// @return The predicted address of the minimal forwarder deployed with the given salt\n function predictAddress(address deployer, bytes32 salt) internal pure returns (address) {\n return keccak256(abi.encodePacked(hex\"ff\", deployer, salt, FORWARDER_INIT_CODE_HASH)).bytes32ToAddress();\n }\n}\n\n\n\n\n\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\n\n\n\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\n\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\n\n\n\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n\n\ncontract SynapseCCTP is SynapseCCTPFees, Pausable, SynapseCCTPEvents, ISynapseCCTP {\n using EnumerableSet for EnumerableSet.AddressSet;\n using MinimalForwarderLib for address;\n using SafeERC20 for IERC20;\n using TypeCasts for address;\n using TypeCasts for bytes32;\n\n /// @notice Struct defining the configuration of a remote domain that has SynapseCCTP deployed.\n /// @dev CCTP uses the following convention for domain numbers:\n /// - 0: Ethereum Mainnet\n /// - 1: Avalanche Mainnet\n /// With more chains added, the convention will be extended.\n /// @param domain Value for the remote domain used in CCTP messages.\n /// @param synapseCCTP Address of the SynapseCCTP deployed on the remote chain.\n struct DomainConfig {\n uint32 domain;\n address synapseCCTP;\n }\n\n /// @notice Refers to the local domain number used in CCTP messages.\n uint32 public immutable localDomain;\n IMessageTransmitter public immutable messageTransmitter;\n ITokenMessenger public immutable tokenMessenger;\n\n // (chainId =\u003e configuration of the remote chain)\n mapping(uint256 =\u003e DomainConfig) public remoteDomainConfig;\n // (Circle token =\u003e liquidity pool with the token)\n mapping(address =\u003e address) public circleTokenPool;\n\n constructor(ITokenMessenger tokenMessenger_, address owner_) {\n tokenMessenger = tokenMessenger_;\n messageTransmitter = IMessageTransmitter(tokenMessenger_.localMessageTransmitter());\n localDomain = messageTransmitter.localDomain();\n _transferOwnership(owner_);\n }\n\n // ═════════════════════════════════════════════ SET CONFIG LOGIC ══════════════════════════════════════════════════\n\n /// @notice Sets the remote domain and deployment of SynapseCCTP for the given remote chainId.\n function setRemoteDomainConfig(\n uint256 remoteChainId,\n uint32 remoteDomain,\n address remoteSynapseCCTP\n ) external onlyOwner {\n // ChainId should be non-zero and different from the local chain id.\n if (remoteChainId == 0 || remoteChainId == block.chainid) revert CCTPIncorrectChainId();\n // Remote domain should differ from the local domain.\n if (remoteDomain == localDomain) revert CCTPIncorrectDomain();\n // Remote domain should be 0 IF AND ONLY IF remote chain id is 1 (Ethereum Mainnet).\n if ((remoteDomain == 0) != (remoteChainId == 1)) revert CCTPIncorrectDomain();\n // Remote SynapseCCTP should be non-zero.\n if (remoteSynapseCCTP == address(0)) revert CCTPZeroAddress();\n remoteDomainConfig[remoteChainId] = DomainConfig(remoteDomain, remoteSynapseCCTP);\n }\n\n /// @notice Sets the liquidity pool for the given Circle token.\n function setCircleTokenPool(address circleToken, address pool) external onlyOwner {\n if (circleToken == address(0)) revert CCTPZeroAddress();\n if (!_bridgeTokens.contains(circleToken)) revert CCTPTokenNotFound();\n // Pool address can be zero if no swaps are supported for the Circle token.\n circleTokenPool[circleToken] = pool;\n }\n\n /// @notice Allows the contract owner to pause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function pauseSending() external onlyOwner {\n _pause();\n }\n\n /// @notice Allows the contract owner to unpause the sending of CCTP tokens.\n /// Note: this does not affect the receiving of CCTP tokens.\n function unpauseSending() external onlyOwner {\n _unpause();\n }\n\n // ═════════════════════════════════════════════ FEES WITHDRAWING ══════════════════════════════════════════════════\n\n /// @notice Allows the owner to withdraw accumulated protocol fees.\n function withdrawProtocolFees(address token) external onlyOwner {\n uint256 accFees = accumulatedFees[address(0)][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[address(0)][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n /// @notice Allows the Relayer's fee collector to withdraw accumulated relayer fees.\n function withdrawRelayerFees(address token) external {\n uint256 accFees = accumulatedFees[msg.sender][token];\n if (accFees == 0) revert CCTPZeroAmount();\n accumulatedFees[msg.sender][token] = 0;\n IERC20(token).safeTransfer(msg.sender, accFees);\n }\n\n // ════════════════════════════════════════════════ CCTP LOGIC ═════════════════════════════════════════════════════\n\n /// @inheritdoc ISynapseCCTP\n function sendCircleToken(\n address recipient,\n uint256 chainId,\n address burnToken,\n uint256 amount,\n uint32 requestVersion,\n bytes memory swapParams\n ) external whenNotPaused {\n // Check if token is supported before doing anything else.\n if (!_bridgeTokens.contains(burnToken)) revert CCTPTokenNotFound();\n // Pull token from user and update the amount in case of transfer fee.\n amount = _pullToken(burnToken, amount);\n uint64 nonce = messageTransmitter.nextAvailableNonce();\n // This will revert if the request version is not supported, or swap params are not properly formatted.\n bytes memory formattedRequest = RequestLib.formatRequest(\n requestVersion,\n RequestLib.formatBaseRequest(localDomain, nonce, burnToken, amount, recipient),\n swapParams\n );\n DomainConfig memory config = remoteDomainConfig[chainId];\n bytes32 dstSynapseCCTP = config.synapseCCTP.addressToBytes32();\n if (dstSynapseCCTP == 0) revert RemoteCCTPDeploymentNotSet();\n uint32 destinationDomain = config.domain;\n // Construct the request identifier to be used as salt later.\n // The identifier (requestID) is unique for every single request on all the chains.\n // This is done by including origin and destination domains as well as origin nonce in the hashed data.\n // Origin domain and nonce are included in `formattedRequest`, so we only need to add the destination domain.\n bytes32 requestID = _requestID(destinationDomain, requestVersion, formattedRequest);\n // Issue allowance if needed\n _approveToken(burnToken, address(tokenMessenger), amount);\n tokenMessenger.depositForBurnWithCaller(\n amount,\n destinationDomain,\n dstSynapseCCTP,\n burnToken,\n _destinationCaller(dstSynapseCCTP.bytes32ToAddress(), requestID)\n );\n // We want to emit the EOA address that initiated the transaction as \"sender\",\n // so we use `tx.origin` instead of `msg.sender`.\n // Note: this is done for analytics only, and should NOT be used by off-chain actors\n // for security purposes.\n // solhint-disable avoid-tx-origin\n emit CircleRequestSent(\n chainId,\n tx.origin,\n nonce,\n burnToken,\n amount,\n requestVersion,\n formattedRequest,\n requestID\n );\n }\n\n /// @inheritdoc ISynapseCCTP\n function receiveCircleToken(\n bytes calldata message,\n bytes calldata signature,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) external payable {\n // Check that the Relayer provided correct `msg.value`\n if (msg.value != chainGasAmount) revert CCTPIncorrectGasAmount();\n (bytes memory baseRequest, bytes memory swapParams) = RequestLib.decodeRequest(\n requestVersion,\n formattedRequest\n );\n (uint32 originDomain, , address originBurnToken, uint256 amount, address recipient) = RequestLib\n .decodeBaseRequest(baseRequest);\n // For requestID hashing we use origin and destination domains as well as origin nonce.\n // This ensures that requestID is unique for each request, and that it is not possible to replay requests.\n bytes32 requestID = _requestID(localDomain, requestVersion, formattedRequest);\n // Kindly ask the Circle Bridge to mint the tokens for us.\n _mintCircleToken(message, signature, requestID);\n address token = _getLocalToken(originDomain, originBurnToken);\n uint256 fee;\n // Apply the bridging fee. This will revert if amount \u003c= fee.\n (amount, fee) = _applyRelayerFee(token, amount, requestVersion == RequestLib.REQUEST_SWAP);\n // Fulfill the request: perform an optional swap and send the end tokens to the recipient.\n (address tokenOut, uint256 amountOut) = _fulfillRequest(recipient, token, amount, swapParams);\n // Perform the gas airdrop and emit corresponding event if gas airdrop is enabled\n if (msg.value \u003e 0) _transferMsgValue(recipient);\n emit CircleRequestFulfilled(originDomain, recipient, token, fee, tokenOut, amountOut, requestID);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /// @notice Get the local token associated with the given remote domain and token.\n function getLocalToken(uint32 remoteDomain, address remoteToken) external view returns (address) {\n return _getLocalToken(remoteDomain, remoteToken);\n }\n\n /// @notice Checks if the given request is already fulfilled.\n function isRequestFulfilled(bytes32 requestID) external view returns (bool) {\n // Request is fulfilled if the requestID is already used, meaning the forwarder is already deployed.\n return MinimalForwarderLib.predictAddress(address(this), requestID).code.length \u003e 0;\n }\n\n // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════\n\n /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance.\n /// Doesn't modify the allowance if it's already enough for the given amount.\n function _approveToken(\n address token,\n address spender,\n uint256 amount\n ) internal {\n uint256 allowance = IERC20(token).allowance(address(this), spender);\n if (allowance \u003c amount) {\n // Reset allowance to 0 before setting it to the new value.\n if (allowance != 0) IERC20(token).safeApprove(spender, 0);\n IERC20(token).safeApprove(spender, type(uint256).max);\n }\n }\n\n /// @dev Pulls the token from the sender.\n function _pullToken(address token, uint256 amount) internal returns (uint256 amountPulled) {\n uint256 balanceBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n amountPulled = IERC20(token).balanceOf(address(this)) - balanceBefore;\n }\n\n /// @dev Mints the Circle token by sending the message and signature to the Circle Bridge.\n function _mintCircleToken(\n bytes calldata message,\n bytes calldata signature,\n bytes32 requestID\n ) internal {\n // Deploy a forwarder specific to this request. Will revert if the requestID has been used before.\n address forwarder = MinimalForwarderLib.deploy(requestID);\n // Form the payload for the Circle Bridge.\n bytes memory payload = abi.encodeWithSelector(IMessageTransmitter.receiveMessage.selector, message, signature);\n // Use the deployed forwarder (who is the only one who can call the Circle Bridge for this message)\n // This will revert if the provided message is not properly formatted, or if the signatures are invalid.\n bytes memory returnData = forwarder.forwardCall(address(messageTransmitter), payload);\n // messageTransmitter.receiveMessage is supposed to return true if the message was received.\n if (!abi.decode(returnData, (bool))) revert CCTPMessageNotReceived();\n }\n\n /// @dev Performs a swap, if was requested back on origin chain, and transfers the tokens to the recipient.\n /// Should the swap fail, will transfer `token` to the recipient instead.\n function _fulfillRequest(\n address recipient,\n address token,\n uint256 amount,\n bytes memory swapParams\n ) internal returns (address tokenOut, uint256 amountOut) {\n // Fallback to Base Request if no swap params are provided\n if (swapParams.length == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // We checked request version to be a valid value when wrapping into `request`,\n // so this could only be `RequestLib.REQUEST_SWAP`.\n address pool = circleTokenPool[token];\n // Fallback to Base Request if no pool is found\n if (pool == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n (uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut) = RequestLib\n .decodeSwapParams(swapParams);\n tokenOut = _tryGetToken(pool, tokenIndexTo);\n // Fallback to Base Request if failed to get tokenOut address\n if (tokenOut == address(0)) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Approve the pool to spend the token, if needed.\n _approveToken(token, pool, amount);\n amountOut = _trySwap(pool, tokenIndexFrom, tokenIndexTo, amount, deadline, minAmountOut);\n // Fallback to Base Request if failed to swap\n if (amountOut == 0) {\n IERC20(token).safeTransfer(recipient, amount);\n return (token, amount);\n }\n // Transfer the swapped tokens to the recipient.\n IERC20(tokenOut).safeTransfer(recipient, amountOut);\n }\n\n /// @dev Tries to swap tokens using the provided swap instructions.\n /// Instead of reverting, returns 0 if the swap failed.\n function _trySwap(\n address pool,\n uint8 tokenIndexFrom,\n uint8 tokenIndexTo,\n uint256 amount,\n uint256 deadline,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n try IDefaultPool(pool).swap(tokenIndexFrom, tokenIndexTo, amount, minAmountOut, deadline) returns (\n uint256 amountOut_\n ) {\n amountOut = amountOut_;\n } catch {\n // Swapping failed, return 0\n amountOut = 0;\n }\n }\n\n // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════\n\n /// @dev Gets the address of the local minted Circle token from the local TokenMinter.\n function _getLocalToken(uint32 remoteDomain, address remoteToken) internal view returns (address token) {\n ITokenMinter minter = ITokenMinter(tokenMessenger.localMinter());\n token = minter.getLocalToken(remoteDomain, remoteToken.addressToBytes32());\n // Revert if TokenMinter is not aware of this remote token.\n if (token == address(0)) revert CCTPTokenNotFound();\n }\n\n /// @dev Tries to get the token address from the pool.\n /// Instead of reverting, returns 0 if the getToken failed.\n function _tryGetToken(address pool, uint8 tokenIndex) internal view returns (address token) {\n // Issue a low level static call instead of IDefaultPool(pool).getToken(tokenIndex)\n // to ensure this never reverts\n (bool success, bytes memory returnData) = pool.staticcall(\n abi.encodeWithSelector(IDefaultPool.getToken.selector, tokenIndex)\n );\n if (success \u0026\u0026 returnData.length == 32) {\n // Do the casting instead of using abi.decode to discard the dirty highest bits if there are any\n token = bytes32(returnData).bytes32ToAddress();\n } else {\n // Return 0 on revert or if pool returned something unexpected\n token = address(0);\n }\n }\n\n /// @dev Predicts the address of the destination caller that will be used to call the Circle Message Transmitter.\n function _destinationCaller(address synapseCCTP, bytes32 requestID) internal pure returns (bytes32) {\n // On the destination chain, Synapse CCTP will deploy a MinimalForwarder for each request,\n // using requestID as salt for the create2 deployment.\n return synapseCCTP.predictAddress(requestID).addressToBytes32();\n }\n\n /// @dev Calculates the unique identifier of the request.\n function _requestID(\n uint32 destinationDomain,\n uint32 requestVersion,\n bytes memory formattedRequest\n ) internal pure returns (bytes32 requestID) {\n // Merge the destination domain and the request version into a single uint256.\n uint256 prefix = (uint256(destinationDomain) \u003c\u003c 32) | requestVersion;\n bytes32 requestHash = keccak256(formattedRequest);\n // Use assembly to return hash of the prefix and the request hash.\n // We are using scratch space to avoid unnecessary memory expansion.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Store prefix in memory at 0, and requestHash at 32.\n mstore(0, prefix)\n mstore(32, requestHash)\n // Return hash of first 64 bytes of memory.\n requestID := keccak256(0, 64)\n }\n }\n}\n\n\n\n","language":"Solidity","languageVersion":"0.8.13","compilerVersion":"0.8.13","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"18925:866:0:-:0;;;;;;;;;;;;;;;-1:-1:-1;;;18925:866:0;;;;;;;;;;;;;;;;;","srcMapRuntime":"18925:866:0:-:0;;;;;;;;","abiDefinition":[],"userDoc":{"kind":"user","methods":{},"version":1},"developerDoc":{"kind":"dev","methods":{},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/SynapseCCTPV1_flat.sol\":\"TypeCasts\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/SynapseCCTPV1_flat.sol\":{\"keccak256\":\"0x0f20afd74fdda1d02d9bd628e42f41b745bbd053f5f1ec2362feb24a4bbf1a2b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8a4e5d212515b27cb5fa38b843c2bc0b6a988d55f7866e89125b3f8db63790ef\",\"dweb:/ipfs/QmecvsrgCTWT4AdJHoaoUAoLWxim7BkNXvtg7gpPHwQReP\"]}},\"version\":1}"},"hashes":{}}}