UTB

0x0eb3c930944951876e2e7be8b8fe4759ffd6fb44

Verification
Verified
v0.8.24+commit.e11b9ed9
Type
Contract
15,990 bytes
ABI entries
66
16 read · 19 write
License
none

Contract information

Address
0x0eb3c930944951876e2e7be8b8fe4759ffd6fb44
Chain
Robinhood Chain (4663)
Compiler
v0.8.24+commit.e11b9ed9
Optimization
Enabled
Creation tx
0x252c87a1da…ad05e50a87

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (16)

DEFAULT_ADMIN_ROLE()bytes32
bridgeAdapters(uint8)address
defaultAdmin()address
defaultAdminDelay()uint48
defaultAdminDelayIncreaseWait()uint48
executor()address
feeManager()address
getRoleAdmin(bytes32)bytes32
hasRole(bytes32, address)bool
isActive()bool
owner()address
pendingDefaultAdmin()address, uint48
pendingDefaultAdminDelay()uint48, uint48
supportsInterface(bytes4)bool
swappers(uint8)address
wrapped()address

Events (18)

BridgeCalledDefaultAdminDelayChangeCanceledDefaultAdminDelayChangeScheduledDefaultAdminTransferCanceledDefaultAdminTransferScheduledReceivedFromBridgeRegisteredBridgeAdapterRegisteredSwapperRoleAdminChangedRoleGrantedRoleRevokedSetExecutorSetFeeManagerSetIsActiveSetWrappedSwappedWithdrawWithdrawERC20

ABI

[
  {
    "inputs": [],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "ExpiredFees",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidFees",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidRecipient",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidSwapParams",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotEnoughNative",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OnlyBridgeAdapter",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OnlyWrapped",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ProtocolFeeCannotBeFetched",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "RefundFailed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "UTBPaused",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "WithdrawalFailed",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "txId",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "bytes4",
        "name": "appId",
        "type": "bytes4"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "dstChainId",
        "type": "uint256"
      },
      {
        "components": [
          {
            "internalType": "uint256",
            "name": "amountIn",
            "type": "uint256"
          },
          {
            "internalType": "address",
            "name": "tokenIn",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "tokenOut",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "target",
            "type": "address"
          },
          {
            "internalType": "bytes4",
            "name": "affiliateId",
            "type": "bytes4"
          },
          {
            "components": [
              {
                "internalType": "address",
                "name": "recipient",
                "type": "address"
              },
              {
                "internalType": "address",
                "name": "token",
                "type": "address"
              },
              {
                "internalType": "uint256",
                "name": "amount",
                "type": "uint256"
              }
            ],
            "internalType": "struct Fee[]",
            "name": "fees",
            "type": "tuple[]"
          }
        ],
        "indexed": false,
        "internalType": "struct IUTB.TxInfo",
        "name": "txInfo",
        "type": "tuple"
      }
    ],
    "name": "BridgeCalled",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [],
    "name": "DefaultAdminDelayChangeCanceled",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint48",
        "name": "newDelay",
        "type": "uint48"
      },
      {
        "indexed": false,
        "internalType": "uint48",
        "name": "effectSchedule",
        "type": "uint48"
      }
    ],
    "name": "DefaultAdminDelayChangeScheduled",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [],
    "name": "DefaultAdminTransferCanceled",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "newAdmin",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint48",
        "name": "acceptSchedule",
        "type": "uint48"
      }
    ],
    "name": "DefaultAdminTransferScheduled",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "txId",
        "type": "bytes32"
      }
    ],
    "name": "ReceivedFromBridge",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "bridge",
        "type": "address"
      }
    ],
    "name": "RegisteredBridgeAdapter",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "swapper",
        "type": "address"
      }
    ],
    "name": "RegisteredSwapper",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "previousAdminRole",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "newAdminRole",
        "type": "bytes32"
      }
    ],
    "name": "RoleAdminChanged",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "account",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "sender",
        "type": "address"
      }
    ],
    "name": "RoleGranted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "account",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "sender",
        "type": "address"
      }
    ],
    "name": "RoleRevoked",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "executor",
        "type": "address"
      }
    ],
    "name": "SetExecutor",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "feeManager",
        "type": "address"
      }
    ],
    "name": "SetFeeManager",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "bool",
        "name": "isActive",
        "type": "bool"
      }
    ],
    "name": "SetIsActive",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "wrapped",
        "type": "address"
      }
    ],
    "name": "SetWrapped",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "txId",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "bytes4",
        "name": "appId",
        "type": "bytes4"
      },
      {
        "components": [
          {
            "internalType": "uint256",
            "name": "amountIn",
            "type": "uint256"
          },
          {
            "internalType": "address",
            "name": "tokenIn",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "tokenOut",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "target",
            "type": "address"
          },
          {
            "internalType": "bytes4",
            "name": "affiliateId",
            "type": "bytes4"
          },
          {
            "components": [
              {
                "internalType": "address",
                "name": "recipient",
                "type": "address"
              },
              {
                "internalType": "address",
                "name": "token",
                "type": "address"
              },
              {
                "internalType": "uint256",
                "name": "amount",
                "type": "uint256"
              }
            ],
            "internalType": "struct Fee[]",
            "name": "fees",
            "type": "tuple[]"
          }
        ],
        "indexed": false,
        "internalType": "struct IUTB.TxInfo",
        "name": "txInfo",
        "type": "tuple"
      }
    ],
    "name": "Swapped",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "Withdraw",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "WithdrawERC20",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "DEFAULT_ADMIN_ROLE",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "acceptDefaultAdminTransfer",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newAdmin",
        "type": "address"
      }
    ],
    "name": "beginDefaultAdminTransfer",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint8",
        "name": "id",
        "type": "uint8"
      }
    ],
    "name": "bridgeAdapters",
    "outputs": [
      {
        "internalType": "address",
        "name": "bridgeAdapter",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "components": [
              {
                "internalType": "uint8",
                "name": "swapperId",
                "type": "uint8"
              },
              {
                "components": [
                  {
                    "internalType": "uint256",
                    "name": "amountIn",
                    "type": "uint256"
                  },
                  {
                    "internalType": "uint256",
                    "name": "amountOut",
                    "type": "uint256"
                  },
                  {
                    "internalType": "uint256",
                    "name": "dustOut",
                    "type": "uint256"
                  },
                  {
                    "internalType": "address",
                    "name": "tokenIn",
                    "type": "address"
                  },
                  {
                    "internalType": "address",
                    "name": "tokenOut",
                    "type": "address"
                  },
                  {
                    "internalType": "uint8",
                    "name": "direction",
                    "type": "uint8"
                  },
                  {
                    "internalType": "address",
                    "name": "refund",
                    "type": "address"
                  },
                  {
                    "internalType": "bytes",
                    "name": "additionalArgs",
                    "type": "bytes"
                  }
                ],
                "internalType": "struct SwapParams",
                "name": "swapParams",
                "type": "tuple"
              }
            ],
            "internalType": "struct SwapInstructions",
            "name": "preBridge",
            "type": "tuple"
          },
          {
            "components": [
              {
                "internalType": "uint8",
                "name": "swapperId",
                "type": "uint8"
              },
              {
                "components": [
                  {
                    "internalType": "uint256",
                    "name": "amountIn",
                    "type": "uint256"
                  },
                  {
                    "internalType": "uint256",
                    "name": "amountOut",
                    "type": "uint256"
                  },
                  {
                    "internalType": "uint256",
                    "name": "dustOut",
                    "type": "uint256"
                  },
                  {
                    "internalType": "address",
                    "name": "tokenIn",
                    "type": "address"
                  },
                  {
                    "internalType": "address",
                    "name": "tokenOut",
                    "type": "address"
                  },
                  {
                    "internalType": "uint8",
                    "name": "direction",
                    "type": "uint8"
                  },
                  {
                    "internalType": "address",
                    "name": "refund",
                    "type": "address"
                  },
                  {
                    "internalType": "bytes",
                    "name": "additionalArgs",
                    "type": "bytes"
                  }
                ],
                "internalType": "struct SwapParams",
                "name": "swapParams",
                "type": "tuple"
              }
            ],
            "internalType": "struct SwapInstructions",
            "name": "postBridge",
            "type": "tuple"
          },
          {
            "internalType": "uint8",
            "name": "bridgeId",
            "type": "uint8"
          },
          {
            "internalType": "uint256",
            "name": "dstChainId",
            "type": "uint256"
          },
          {
            "internalType": "address",
            "name": "target",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "paymentOperator",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "refund",
            "type": "address"
          },
          {
            "internalType": "bytes",
            "name": "payload",
            "type": "bytes"
          },
          {
            "internalType": "bytes",
            "name": "additionalArgs",
            "type": "bytes"
          },
          {
            "internalType": "bytes32",
            "name": "txId",
            "type": "bytes32"
          }
        ],
        "internalType": "struct BridgeInstructions",
        "name": "instructions",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "bytes4",
            "name": "appId",
            "type": "bytes4"
          },
          {
            "internalType": "bytes4",
            "name": "affiliateId",
            "type": "bytes4"
          },
          {
            "internalType": "uint256",
            "name": "bridgeFee",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "deadline",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "chainId",
            "type": "uint256"
          },
          {
            "components": [
              {
                "internalType": "address",
                "name": "recipient",
                "type": "address"
              },
              {
                "internalType": "address",
                "name": "token",
                "type": "address"
              },
              {
                "internalType": "uint256",
                "name": "amount",
                "type": "uint256"
              }
            ],
            "internalType": "struct Fee[]",
            "name": "appFees",
            "type": "tuple[]"
          }
        ],
        "internalType": "struct FeeData",
        "name": "feeData",
        "type": "tuple"
      },
      {
        "internalType": "bytes",
        "name": "signature",
        "type": "bytes"
      }
    ],
    "name": "bridgeAndExecute",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "cancelDefaultAdminTransfer",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint48",
        "name": "newDelay",
        "type": "uint48"
      }
    ],
    "name": "changeDefaultAdminDelay",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "defaultAdmin",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "defaultAdminDelay",
    "outputs": [
      {
        "internalType": "uint48",
        "name": "",
        "type": "uint48"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "defaultAdminDelayIncreaseWait",
    "outputs": [
      {
        "internalType": "uint48",
        "name": "",
        "type": "uint48"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "executor",
    "outputs": [
      {
        "internalType": "contract IUTBExecutor",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "feeManager",
    "outputs": [
      {
        "internalType": "contract IUTBFeeManager",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      }
    ],
    "name": "getRoleAdmin",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "grantRole",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "hasRole",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "isActive",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "owner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pendingDefaultAdmin",
    "outputs": [
      {
        "internalType": "address",
        "name": "newAdmin",
        "type": "address"
      },
      {
        "internalType": "uint48",
        "name": "schedule",
        "type": "uint48"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pendingDefaultAdminDelay",
    "outputs": [
      {
        "internalType": "uint48",
        "name": "newDelay",
        "type": "uint48"
      },
      {
        "internalType": "uint48",
        "name": "schedule",
        "type": "uint48"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "uint8",
            "name": "swapperId",
            "type": "uint8"
          },
          {
            "components": [
              {
                "internalType": "uint256",
                "name": "amountIn",
                "type": "uint256"
              },
              {
                "internalType": "uint256",
                "name": "amountOut",
                "type": "uint256"
              },
              {
                "internalType": "uint256",
                "name": "dustOut",
                "type": "uint256"
              },
              {
                "internalType": "address",
                "name": "tokenIn",
                "type": "address"
              },
              {
                "internalType": "address",
                "name": "tokenOut",
                "type": "address"
              },
              {
                "internalType": "uint8",
                "name": "direction",
                "type": "uint8"
              },
              {
                "internalType": "address",
                "name": "refund",
                "type": "address"
              },
              {
                "internalType": "bytes",
                "name": "additionalArgs",
                "type": "bytes"
              }
            ],
            "internalType": "struct SwapParams",
            "name": "swapParams",
            "type": "tuple"
          }
        ],
        "internalType": "struct SwapInstructions",
        "name": "postBridge",
        "type": "tuple"
      },
      {
        "internalType": "address",
        "name": "target",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "paymentOperator",
        "type": "address"
      },
      {
        "internalType": "bytes",
        "name": "payload",
        "type": "bytes"
      },
      {
        "internalType": "address",
        "name": "refund",
        "type": "address"
      },
      {
        "internalType": "uint8",
        "name": "bridgeId",
        "type": "uint8"
      },
      {
        "internalType": "bytes32",
        "name": "txId",
        "type": "bytes32"
      }
    ],
    "name": "receiveFromBridge",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "bridge",
        "type": "address"
      }
    ],
    "name": "registerBridge",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "swapper",
        "type": "address"
      }
    ],
    "name": "registerSwapper",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "renounceRole",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "revokeRole",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "rollbackDefaultAdminDelay",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "_executor",
        "type": "address"
      }
    ],
    "name": "setExecutor",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "_feeManager",
        "type": "address"
      }
    ],
    "name": "setFeeManager",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "_wrapped",
        "type": "address"
      }
    ],
    "name": "setWrapped",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes4",
        "name": "interfaceId",
        "type": "bytes4"
      }
    ],
    "name": "supportsInterface",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "components": [
              {
                "internalType": "uint8",
                "name": "swapperId",
                "type": "uint8"
              },
              {
                "components": [
                  {
                    "internalType": "uint256",
                    "name": "amountIn",
                    "type": "uint256"
                  },
                  {
                    "internalType": "uint256",
                    "name": "amountOut",
                    "type": "uint256"
                  },
                  {
                    "internalType": "uint256",
                    "name": "dustOut",
                    "type": "uint256"
                  },
                  {
                    "internalType": "address",
                    "name": "tokenIn",
                    "type": "address"
                  },
                  {
                    "internalType": "address",
                    "name": "tokenOut",
                    "type": "address"
                  },
                  {
                    "internalType": "uint8",
                    "name": "direction",
                    "type": "uint8"
                  },
                  {
                    "internalType": "address",
                    "name": "refund",
                    "type": "address"
                  },
                  {
                    "internalType": "bytes",
                    "name": "additionalArgs",
                    "type": "bytes"
                  }
                ],
                "internalType": "struct SwapParams",
                "name": "swapParams",
                "type": "tuple"
              }
            ],
            "internalType": "struct SwapInstructions",
            "name": "swapInstructions",
            "type": "tuple"
          },
          {
            "internalType": "address",
            "name": "target",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "paymentOperator",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "refund",
            "type": "address"
          },
          {
            "internalType": "uint256",
            "name": "executionFee",
            "type": "uint256"
          },
          {
            "internalType": "bytes",
            "name": "payload",
            "type": "bytes"
          },
          {
            "internalType": "bytes32",
            "name": "txId",
            "type": "bytes32"
          }
        ],
        "internalType": "struct SwapAndExecuteInstructions",
        "name": "instructions",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "bytes4",
            "name": "appId",
            "type": "bytes4"
          },
          {
            "internalType": "bytes4",
            "name": "affiliateId",
            "type": "bytes4"
          },
          {
            "internalType": "uint256",
            "name": "bridgeFee",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "deadline",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "chainId",
            "type": "uint256"
          },
          {
            "components": [
              {
                "internalType": "address",
                "name": "recipient",
                "type": "address"
              },
              {
                "internalType": "address",
                "name": "token",
                "type": "address"
              },
              {
                "internalType": "uint256",
                "name": "amount",
                "type": "uint256"
              }
            ],
            "internalType": "struct Fee[]",
            "name": "appFees",
            "type": "tuple[]"
          }
        ],
        "internalType": "struct FeeData",
        "name": "feeData",
        "type": "tuple"
      },
      {
        "internalType": "bytes",
        "name": "signature",
        "type": "bytes"
      }
    ],
    "name": "swapAndExecute",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint8",
        "name": "id",
        "type": "uint8"
      }
    ],
    "name": "swappers",
    "outputs": [
      {
        "internalType": "address",
        "name": "swapper",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "toggleActive",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "withdraw",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "withdrawERC20",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "wrapped",
    "outputs": [
      {
        "internalType": "contract IWETH",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;

import {Roles} from "./utils/Roles.sol";
import {IUTB} from "./interfaces/IUTB.sol";
import {IUTBExecutor} from "./interfaces/IUTBExecutor.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IWETH} from "./interfaces/IWETH.sol";
import {IUTBFeeManager} from "./interfaces/IUTBFeeManager.sol";
import {IBridgeAdapter} from "./interfaces/IBridgeAdapter.sol";
import {ISwapper} from "./interfaces/ISwapper.sol";
import {SwapInstructions, SwapParams, FeeData, Fee, BridgeInstructions, SwapAndExecuteInstructions} from "./CommonTypes.sol";
import {Withdrawable} from "./utils/Withdrawable.sol";

contract UTB is IUTB, Roles, Withdrawable {
    constructor() Roles(msg.sender) {}

    IUTBExecutor public executor;
    IUTBFeeManager public feeManager;
    IWETH public wrapped;
    mapping(uint8 id => address swapper) public swappers;
    mapping(uint8 id => address bridgeAdapter) public bridgeAdapters;
    bool public isActive = true;

    /**
     * @dev only support calling swapAndExecute and bridgeAndExecute if active
     */
    modifier isUtbActive() {
        if (!isActive) revert UTBPaused();
        _;
    }

    modifier onlyWrapped() {
        if (msg.sender != address(wrapped)) revert OnlyWrapped();
        _;
    }

    /**
     * @dev Transfers fees from the sender to the fee recipients.
     * @param feeData The bridge fee in native, as well as utb fee tokens and amounts.
     * @param packedInfo The fees and swap instructions which were used to generate the signature.
     * @param signature The ECDSA signature to verify the fee structure.
     */
    function _retrieveAndCollectFees(
        FeeData calldata feeData,
        SwapParams memory swapParams,
        bytes memory packedInfo,
        bytes calldata signature
    ) private returns (uint256 value) {
        if (feeData.chainId != block.chainid) revert InvalidFees();
        if (block.timestamp > feeData.deadline) revert ExpiredFees();

        if (address(feeManager) != address(0)) {
            feeManager.verifySignature(packedInfo, signature);
            value += feeData.bridgeFee;
            Fee[] memory fees = feeData.appFees;
            for (uint i = 0; i < fees.length; i++) {
                Fee memory fee = fees[i];
                if (fee.token != address(0)) {
                    SafeERC20.safeTransferFrom(
                        IERC20(fee.token),
                        msg.sender,
                        fee.recipient,
                        fee.amount
                    );
                } else {
                    (bool success, ) = address(fee.recipient).call{value: fee.amount}("");
                    value += fee.amount;
                    if (!success) revert ProtocolFeeCannotBeFetched();
                }
            }
        }

        uint256 valueRequired = swapParams.tokenIn == address(0)
            ? swapParams.amountIn + value
            : value;

        if (msg.value < valueRequired) revert NotEnoughNative();
    }

    /**
     * @dev Refunds the specified refund address.
     * @param to The address receiving the refund.
     * @param amount The amount of the refund.
     */
    function _refundUser(address to, address token, uint256 amount) private {
        if ( amount > 0 ) {
            if (token == address(0)) {
                (bool success, ) = to.call{value: amount}("");
                if (!success) revert RefundFailed();
            } else {
                SafeERC20.safeTransfer(IERC20(token), to, amount);
            }
        }
    }

    /**
     * @dev Sets the executor.
     * @param _executor The address of the executor.
     */
    function setExecutor(address _executor) public onlyAdmin {
        executor = IUTBExecutor(_executor);
        emit SetExecutor(_executor);
    }

    /**
     * @dev Sets the wrapped native token.
     * @param _wrapped The address of the wrapped token.
     */
    function setWrapped(address _wrapped) public onlyAdmin {
        wrapped = IWETH(_wrapped);
        emit SetWrapped(_wrapped);
    }

    /**
     * @dev Sets the fee manager.
     * @param _feeManager The address of the fee manager.
     */
    function setFeeManager(address _feeManager) public onlyAdmin {
        feeManager = IUTBFeeManager(_feeManager);
        emit SetFeeManager(_feeManager);
    }

    /**
     * @dev toggles active state
     */
    function toggleActive() public onlyAdmin {
        isActive = !isActive;
        emit SetIsActive(isActive);
    }

    /**
     * @dev Checks if there is a swap being performed
     */
    function _isSwapping(SwapParams memory swapParams) private pure returns (bool) {
        return swapParams.additionalArgs.length != 0;
    }

    /**
     * @dev Checks if there is no swap being performed
     */
    function _isNotSwapping(SwapParams memory swapParams) private pure returns (bool) {
        return swapParams.tokenIn == swapParams.tokenOut;
    }

    /**
     * @dev Checks if there is a wrap being performed
     */
    function _isWrapping(SwapParams memory swapParams) private view returns (bool) {
        return swapParams.tokenIn == address(0) && swapParams.tokenOut == address(wrapped);
    }

    /**
     * @dev Checks if there is an unwrap being performed
     */
    function _isUnwrapping(SwapParams memory swapParams) private view returns (bool) {
        return swapParams.tokenIn == address(wrapped) && swapParams.tokenOut == address(0);
    }

    /**
     * @dev Performs a swap with the requested swapper and swap calldata.
     * @param swapInstructions The swapper ID and calldata to execute a swap.
     */
    function _performSwap(
        SwapInstructions memory swapInstructions
    ) private returns (address tokenOut, uint256 amountOut, uint256 value) {
        SwapParams memory swapParams = swapInstructions.swapParams;

        (tokenOut, amountOut, value) = _swapHandler(swapInstructions);

        amountOut -= swapParams.dustOut;

        _refundUser(
            swapParams.refund,
            tokenOut,
            swapParams.dustOut
        );
    }

    /**
     * @dev Routes swap operations to the appropriate handler based on the swap type
     * @param swapInstructions The swap instructions containing swap parameters and swapper ID
     */
    function _swapHandler(
        SwapInstructions memory swapInstructions
    ) private returns (address tokenOut, uint256 amountOut, uint256 value) {
        SwapParams memory swapParams = swapInstructions.swapParams;

        if (_isSwapping(swapParams)) {
            return _handleSwap(swapParams, swappers[swapInstructions.swapperId]);
        }

        if (_isNotSwapping(swapParams)) {
            return _handleNoSwap(swapParams);
        }

        if (_isWrapping(swapParams)) {
            return _handleWrap(swapParams);
        }

        if (_isUnwrapping(swapParams)) {
            return _handleUnwrap(swapParams);
        }

        revert InvalidSwapParams();
    }

    /**
     * @dev Handles a swap operation using the specified swapper contract.
     * @param swapParams The swap parameters containing token addresses and amounts.
     * @param swapper The address of the swapper contract to execute the swap.
     */
    function _handleSwap(
        SwapParams memory swapParams,
        address swapper
    ) private returns (address tokenOut, uint256 amountOut, uint256 value) {
        if (swapParams.tokenIn == address(0)) {
            wrapped.deposit{value: swapParams.amountIn}();
            value = swapParams.amountIn;
            swapParams.tokenIn = address(wrapped);
        } else {
            SafeERC20.safeTransferFrom(
                IERC20(swapParams.tokenIn),
                msg.sender,
                address(this),
                swapParams.amountIn
            );
        }

        SafeERC20.forceApprove(IERC20(swapParams.tokenIn), swapper, swapParams.amountIn);

        (tokenOut, amountOut) = ISwapper(swapper).swap(swapParams);

        if (tokenOut == address(0)) {
            wrapped.withdraw(amountOut);
        }
    }

    /**
     * @dev Handles a direct transfer when input and output tokens are the same.
     * @param swapParams The swap parameters containing token addresses and amounts.
     */
    function _handleNoSwap(
        SwapParams memory swapParams
    ) private returns (address tokenOut, uint256 amountOut, uint256 value) {
        if (swapParams.tokenIn == address(0)) {
            return (address(0), swapParams.amountIn, swapParams.amountIn);
        } else {
            SafeERC20.safeTransferFrom(
                IERC20(swapParams.tokenIn),
                msg.sender,
                address(this),
                swapParams.amountIn
            );
            return (swapParams.tokenIn, swapParams.amountIn, 0);
        }
    }

    /**
     * @dev Handles wrapping of native tokens to wrapped tokens.
     * @param swapParams The swap parameters containing token addresses and amounts.
     */
    function _handleWrap(
        SwapParams memory swapParams
    ) private returns (address tokenOut, uint256 amountOut, uint256 value) {
        wrapped.deposit{value: swapParams.amountIn}();
        return (address(wrapped), swapParams.amountIn, swapParams.amountIn);
    }

    /**
     * @dev Handles unwrapping of wrapped tokens to native tokens.
     * @param swapParams The swap parameters containing token addresses and amounts.
     */
    function _handleUnwrap(
        SwapParams memory swapParams
    ) private returns (address tokenOut, uint256 amountOut, uint256 value) {
        SafeERC20.safeTransferFrom(
            IERC20(wrapped),
            msg.sender,
            address(this),
            swapParams.amountIn
        );
        wrapped.withdraw(swapParams.amountIn);
        return (address(0), swapParams.amountIn, 0);
    }

    /// @inheritdoc IUTB
    function swapAndExecute(
        SwapAndExecuteInstructions calldata instructions,
        FeeData calldata feeData,
        bytes calldata signature
    )
        public
        payable
        isUtbActive
    {
        emit Swapped(
            instructions.txId,
            feeData.appId,
            TxInfo({
                amountIn: instructions.swapInstructions.swapParams.amountIn,
                tokenIn: instructions.swapInstructions.swapParams.tokenIn,
                tokenOut: instructions.swapInstructions.swapParams.tokenOut,
                target: instructions.target,
                affiliateId: feeData.affiliateId,
                fees: feeData.appFees
            })
        );

        uint256 value = _retrieveAndCollectFees(
            feeData,
            instructions.swapInstructions.swapParams,
            abi.encode(instructions, feeData),
            signature
        );

        value += _swapAndExecute(
            instructions.swapInstructions,
            instructions.target,
            instructions.paymentOperator,
            instructions.payload,
            instructions.refund,
            instructions.executionFee
        );

        _refundUser(instructions.refund, address(0), msg.value - value);
    }

    /**
     * @dev Swaps currency from the incoming to the outgoing token and executes a transaction with payment.
     * @param swapInstructions The swapper ID and calldata to execute a swap.
     * @param target The address of the target contract for the payment transaction.
     * @param paymentOperator The operator address for payment transfers requiring ERC20 approvals.
     * @param payload The calldata to execute the payment transaction.
     * @param refund The account receiving any refunds, typically the EOA which initiated the transaction.
     * @param executionFee Forwards additional native fees required for executing the payment transaction.
     */
    function _swapAndExecute(
        SwapInstructions memory swapInstructions,
        address target,
        address paymentOperator,
        bytes memory payload,
        address refund,
        uint256 executionFee
    ) private returns (uint256 value) {
        address tokenOut;
        uint256 amountOut;
        (tokenOut, amountOut, value) = _performSwap(swapInstructions);
        if (executionFee > 0) value += executionFee;
        if (tokenOut == address(0)) {
            executor.execute{value: amountOut + executionFee}(
                target,
                paymentOperator,
                payload,
                tokenOut,
                amountOut,
                refund,
                executionFee
            );
        } else {
            SafeERC20.forceApprove(IERC20(tokenOut), address(executor), amountOut);
            executor.execute{value: executionFee}(
                target,
                paymentOperator,
                payload,
                tokenOut,
                amountOut,
                refund,
                executionFee
            );
        }
    }

    /**
     * @dev Checks if the bridge token is native, and approves the bridge adapter to transfer ERC20 if required.
     * @param instructions The bridge data, token swap data, and payment transaction payload.
     * @param amt2Bridge The amount of the bridge token being transferred to the bridge adapter.
     */
    function _approveAndCheckIfNative(
        BridgeInstructions memory instructions,
        uint256 amt2Bridge
    ) private returns (bool) {
        IBridgeAdapter bridgeAdapter = IBridgeAdapter(bridgeAdapters[instructions.bridgeId]);
        address bridgeToken = bridgeAdapter.getBridgeToken(
            instructions.additionalArgs
        );
        if (bridgeToken != address(0)) {
            SafeERC20.forceApprove(IERC20(bridgeToken), address(bridgeAdapter), amt2Bridge);
            return false;
        }
        return true;
    }

    /// @inheritdoc IUTB
    function bridgeAndExecute(
        BridgeInstructions memory instructions,
        FeeData calldata feeData,
        bytes calldata signature
    )
        public
        payable
        isUtbActive
    {
        emit BridgeCalled(
            instructions.txId,
            feeData.appId,
            instructions.dstChainId,
            TxInfo({
                amountIn: instructions.preBridge.swapParams.amountIn,
                tokenIn: instructions.preBridge.swapParams.tokenIn,
                tokenOut: instructions.postBridge.swapParams.tokenOut,
                target: instructions.target,
                affiliateId: feeData.affiliateId,
                fees: feeData.appFees
            })
        );

        uint256 feeValue = _retrieveAndCollectFees(
            feeData,
            instructions.preBridge.swapParams,
            abi.encode(instructions, feeData),
            signature
        );

        ( , uint256 amt2Bridge, uint256 swapValue) = _performSwap(instructions.preBridge);

        instructions.postBridge.swapParams.amountIn = amt2Bridge;

        _refundUser(instructions.refund, address(0), msg.value - feeValue - swapValue);

        _callBridge(amt2Bridge, feeData.bridgeFee, instructions);
    }

    /**
     * @dev Calls the bridge adapter to bridge funds, and approves the bridge adapter to transfer ERC20 if required.
     * @param amt2Bridge The amount of the bridge token being bridged via the bridge adapter.
     * @param bridgeFee The fee being transferred to the bridge adapter and finally to the bridge.
     * @param instructions The bridge data, token swap data, and payment transaction payload.
     */
    function _callBridge(
        uint256 amt2Bridge,
        uint bridgeFee,
        BridgeInstructions memory instructions
    ) private {
        bool native = _approveAndCheckIfNative(instructions, amt2Bridge);

        IBridgeAdapter(bridgeAdapters[instructions.bridgeId]).bridge{
            value: bridgeFee + (native ? amt2Bridge : 0)
        }(
            IBridgeAdapter.BridgeCall({
                amount: amt2Bridge,
                postBridge: instructions.postBridge,
                dstChainId: instructions.dstChainId,
                target: instructions.target,
                paymentOperator: instructions.paymentOperator,
                payload: instructions.payload,
                additionalArgs: instructions.additionalArgs,
                refund: instructions.refund,
                txId: instructions.txId
            })
        );
    }

    /// @inheritdoc IUTB
    function receiveFromBridge(
        SwapInstructions memory postBridge,
        address target,
        address paymentOperator,
        bytes memory payload,
        address refund,
        uint8 bridgeId,
        bytes32 txId
    ) public payable {
        if (msg.sender != bridgeAdapters[bridgeId]) revert OnlyBridgeAdapter();
        emit ReceivedFromBridge(txId);
        _swapAndExecute(postBridge, target, paymentOperator, payload, refund, 0);
    }

    /// @inheritdoc IUTB
    function registerSwapper(address swapper) public onlyAdmin {
        ISwapper s = ISwapper(swapper);
        swappers[s.ID()] = swapper;
        emit RegisteredSwapper(swapper);
    }

    /// @inheritdoc IUTB
    function registerBridge(address bridge) public onlyAdmin {
        IBridgeAdapter b = IBridgeAdapter(bridge);
        bridgeAdapters[b.ID()] = bridge;
        emit RegisteredBridgeAdapter(bridge);
    }

    receive() external payable onlyWrapped {}
}
Chain explorer1360msChain node84ms