diff --git a/bridge-history-api/abi/backend_abi.go b/bridge-history-api/abi/backend_abi.go index 92553630a..547e63455 100644 --- a/bridge-history-api/abi/backend_abi.go +++ b/bridge-history-api/abi/backend_abi.go @@ -9,15 +9,17 @@ import ( ) var ( - IL1ETHGatewayABI *abi.ABI - IL1ERC20GatewayABI *abi.ABI - IL1ERC721GatewayABI *abi.ABI - IL1ERC1155GatewayABI *abi.ABI + IL1ETHGatewayABI *abi.ABI + IL1ERC20GatewayABI *abi.ABI + IL1ERC721GatewayABI *abi.ABI + IL1ERC1155GatewayABI *abi.ABI + L1BatchBridgeGatewayABI *abi.ABI - IL2ETHGatewayABI *abi.ABI - IL2ERC20GatewayABI *abi.ABI - IL2ERC721GatewayABI *abi.ABI - IL2ERC1155GatewayABI *abi.ABI + IL2ETHGatewayABI *abi.ABI + IL2ERC20GatewayABI *abi.ABI + IL2ERC721GatewayABI *abi.ABI + IL2ERC1155GatewayABI *abi.ABI + L2BatchBridgeGatewayABI *abi.ABI IL1ScrollMessengerABI *abi.ABI IL2ScrollMessengerABI *abi.ABI @@ -32,13 +34,16 @@ var ( L1BatchDepositERC721Sig common.Hash L1DepositERC1155Sig common.Hash L1BatchDepositERC1155Sig common.Hash + L1BridgeBatchDepositSig common.Hash - L2WithdrawETHSig common.Hash - L2WithdrawERC20Sig common.Hash - L2WithdrawERC721Sig common.Hash - L2BatchWithdrawERC721Sig common.Hash - L2WithdrawERC1155Sig common.Hash - L2BatchWithdrawERC1155Sig common.Hash + L2WithdrawETHSig common.Hash + L2WithdrawERC20Sig common.Hash + L2WithdrawERC721Sig common.Hash + L2BatchWithdrawERC721Sig common.Hash + L2WithdrawERC1155Sig common.Hash + L2BatchWithdrawERC1155Sig common.Hash + L2BridgeBatchDistributeSig common.Hash + L2BridgeBatchDistributeFailedSig common.Hash L1SentMessageEventSig common.Hash L1RelayedMessageEventSig common.Hash @@ -61,6 +66,7 @@ func init() { IL1ERC20GatewayABI, _ = IL1ERC20GatewayMetaData.GetAbi() IL1ERC721GatewayABI, _ = IL1ERC721GatewayMetaData.GetAbi() IL1ERC1155GatewayABI, _ = IL1ERC1155GatewayMetaData.GetAbi() + L1BatchBridgeGatewayABI, _ = L1BatchBridgeGatewayMetaData.GetAbi() L1DepositETHSig = IL1ETHGatewayABI.Events["DepositETH"].ID L1DepositERC20Sig = IL1ERC20GatewayABI.Events["DepositERC20"].ID @@ -68,11 +74,13 @@ func init() { L1BatchDepositERC721Sig = IL1ERC721GatewayABI.Events["BatchDepositERC721"].ID L1DepositERC1155Sig = IL1ERC1155GatewayABI.Events["DepositERC1155"].ID L1BatchDepositERC1155Sig = IL1ERC1155GatewayABI.Events["BatchDepositERC1155"].ID + L1BridgeBatchDepositSig = L1BatchBridgeGatewayABI.Events["Deposit"].ID IL2ETHGatewayABI, _ = IL2ETHGatewayMetaData.GetAbi() IL2ERC20GatewayABI, _ = IL2ERC20GatewayMetaData.GetAbi() IL2ERC721GatewayABI, _ = IL2ERC721GatewayMetaData.GetAbi() IL2ERC1155GatewayABI, _ = IL2ERC1155GatewayMetaData.GetAbi() + L2BatchBridgeGatewayABI, _ = L2BatchBridgeGatewayMetaData.GetAbi() L2WithdrawETHSig = IL2ETHGatewayABI.Events["WithdrawETH"].ID L2WithdrawERC20Sig = IL2ERC20GatewayABI.Events["WithdrawERC20"].ID @@ -80,6 +88,8 @@ func init() { L2BatchWithdrawERC721Sig = IL2ERC721GatewayABI.Events["BatchWithdrawERC721"].ID L2WithdrawERC1155Sig = IL2ERC1155GatewayABI.Events["WithdrawERC1155"].ID L2BatchWithdrawERC1155Sig = IL2ERC1155GatewayABI.Events["BatchWithdrawERC1155"].ID + L2BridgeBatchDistributeSig = L2BatchBridgeGatewayABI.Events["BatchDistribute"].ID + L2BridgeBatchDistributeFailedSig = L2BatchBridgeGatewayABI.Events["DistributeFailed"].ID IL1ScrollMessengerABI, _ = IL1ScrollMessengerMetaData.GetAbi() @@ -154,6 +164,16 @@ var IL1MessageQueueMetaData = &bind.MetaData{ ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"skippedBitmap\",\"type\":\"uint256\"}],\"name\":\"DequeueTransaction\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"DropTransaction\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"queueIndex\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"QueueTransaction\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"appendCrossDomainMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"appendEnforcedTransaction\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_calldata\",\"type\":\"bytes\"}],\"name\":\"calculateIntrinsicGasFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"queueIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"computeTransactionHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"dropCrossDomainMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"}],\"name\":\"estimateCrossDomainMessageFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"queueIndex\",\"type\":\"uint256\"}],\"name\":\"getCrossDomainMessage\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"queueIndex\",\"type\":\"uint256\"}],\"name\":\"isMessageDropped\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"queueIndex\",\"type\":\"uint256\"}],\"name\":\"isMessageSkipped\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nextCrossDomainMessageIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pendingQueueIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"skippedBitmap\",\"type\":\"uint256\"}],\"name\":\"popCrossDomainMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } +// L1BatchBridgeGatewayMetaData contains all meta data concerning the L1BatchBridgeGateway contract. +var L1BatchBridgeGatewayMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"constructor\",\"inputs\":[{\"name\":\"_counterpart\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_router\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_messenger\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_queue\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"receive\",\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"DEFAULT_ADMIN_ROLE\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"KEEPER_ROLE\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"batches\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"amount\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"startTime\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"numDeposits\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"hash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"configs\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"feeAmountPerTx\",\"type\":\"uint96\",\"internalType\":\"uint96\"},{\"name\":\"minAmountPerTx\",\"type\":\"uint96\",\"internalType\":\"uint96\"},{\"name\":\"maxTxsPerBatch\",\"type\":\"uint16\",\"internalType\":\"uint16\"},{\"name\":\"maxDelayPerBatch\",\"type\":\"uint24\",\"internalType\":\"uint24\"},{\"name\":\"safeBridgeGasLimit\",\"type\":\"uint24\",\"internalType\":\"uint24\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"counterpart\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"depositERC20\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint96\",\"internalType\":\"uint96\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"depositETH\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"executeBatchDeposit\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"feeVault\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getRoleAdmin\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getRoleMember\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getRoleMemberCount\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"grantRole\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"hasRole\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"_feeVault\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"messenger\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"queue\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"renounceRole\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"revokeRole\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"router\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"setBatchConfig\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"newConfig\",\"type\":\"tuple\",\"internalType\":\"structL1BatchBridgeGateway.BatchConfig\",\"components\":[{\"name\":\"feeAmountPerTx\",\"type\":\"uint96\",\"internalType\":\"uint96\"},{\"name\":\"minAmountPerTx\",\"type\":\"uint96\",\"internalType\":\"uint96\"},{\"name\":\"maxTxsPerBatch\",\"type\":\"uint16\",\"internalType\":\"uint16\"},{\"name\":\"maxDelayPerBatch\",\"type\":\"uint24\",\"internalType\":\"uint24\"},{\"name\":\"safeBridgeGasLimit\",\"type\":\"uint24\",\"internalType\":\"uint24\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"supportsInterface\",\"inputs\":[{\"name\":\"interfaceId\",\"type\":\"bytes4\",\"internalType\":\"bytes4\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"tokens\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"pending\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"currentBatchIndex\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"pendingBatchIndex\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"event\",\"name\":\"BatchDeposit\",\"inputs\":[{\"name\":\"caller\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"l1Token\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"batchIndex\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"},{\"name\":\"l2Token\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Deposit\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"token\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"batchIndex\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"fee\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RoleAdminChanged\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"previousAdminRole\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"newAdminRole\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RoleGranted\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"account\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"sender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RoleRevoked\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"account\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"sender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"ErrorCallerNotMessenger\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorDepositAmountTooSmall\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorIncorrectMethodForETHDeposit\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorInsufficientMsgValueForBatchDepositFee\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorInvalidBatchConfig\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorNoPendingBatch\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorTokenNotSupported\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorTransferETHFailed\",\"inputs\":[]}]", +} + +// L2BatchBridgeGatewayMetaData contains all meta data concerning the L2BatchBridgeGateway contract. +var L2BatchBridgeGatewayMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"constructor\",\"inputs\":[{\"name\":\"_counterpart\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_messenger\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"receive\",\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"DEFAULT_ADMIN_ROLE\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"KEEPER_ROLE\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"batchHashes\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"counterpart\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"distribute\",\"inputs\":[{\"name\":\"l2Token\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"batchIndex\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"nodes\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"failedAmount\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"finalizeBatchDeposit\",\"inputs\":[{\"name\":\"l1Token\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l2Token\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"batchIndex\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"hash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"getRoleAdmin\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getRoleMember\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getRoleMemberCount\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"grantRole\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"hasRole\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"isDistributed\",\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"messenger\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"renounceRole\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"revokeRole\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"supportsInterface\",\"inputs\":[{\"name\":\"interfaceId\",\"type\":\"bytes4\",\"internalType\":\"bytes4\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"tokenMapping\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"updateTokenMapping\",\"inputs\":[{\"name\":\"l2Token\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l1Token\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"withdrawFailedAmount\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"receiver\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"event\",\"name\":\"BatchDistribute\",\"inputs\":[{\"name\":\"l1Token\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"l2Token\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"batchIndex\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"DistributeFailed\",\"inputs\":[{\"name\":\"l2Token\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"batchIndex\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"},{\"name\":\"receiver\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"FinalizeBatchDeposit\",\"inputs\":[{\"name\":\"l1Token\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"l2Token\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"batchIndex\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RoleAdminChanged\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"previousAdminRole\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"newAdminRole\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RoleGranted\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"account\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"sender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RoleRevoked\",\"inputs\":[{\"name\":\"role\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"account\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"sender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"UpdateTokenMapping\",\"inputs\":[{\"name\":\"l2Token\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"oldL1Token\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newL1Token\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"ErrorBatchDistributed\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorBatchHashMismatch\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorCallerNotMessenger\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorL1TokenMismatched\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorMessageSenderNotCounterpart\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ErrorNoFailedDistribution\",\"inputs\":[]}]", +} + type ETHMessageEvent struct { From common.Address To common.Address @@ -269,3 +289,27 @@ type L1DequeueTransactionEvent struct { type L1DropTransactionEvent struct { Index *big.Int } + +// L1BatchBridgeGatewayDeposit represents L1 batch bridge Deposit event +type L1BatchBridgeGatewayDeposit struct { + Sender common.Address + Token common.Address + BatchIndex *big.Int + Amount *big.Int + Fee *big.Int +} + +// L2BatchBridgeGatewayBatchDistribute represents a BatchDistribute event +type L2BatchBridgeGatewayBatchDistribute struct { + L1Token common.Address + L2Token common.Address + BatchIndex *big.Int +} + +// L2BatchBridgeGatewayDistributeFailed represents a DistributeFailed event +type L2BatchBridgeGatewayDistributeFailed struct { + L2Token common.Address + BatchIndex *big.Int + Receiver common.Address + Amount *big.Int +} diff --git a/bridge-history-api/cmd/db_cli/app/app.go b/bridge-history-api/cmd/db_cli/app/app.go index 4722b5802..4e6a3f397 100644 --- a/bridge-history-api/cmd/db_cli/app/app.go +++ b/bridge-history-api/cmd/db_cli/app/app.go @@ -26,32 +26,27 @@ func init() { Name: "reset", Usage: "Clean and reset database.", Action: resetDB, - Flags: []cli.Flag{&utils.ConfigFileFlag}, }, { Name: "status", Usage: "Check migration status.", Action: checkDBStatus, - Flags: []cli.Flag{&utils.ConfigFileFlag}, }, { Name: "version", Usage: "Display the current database version.", Action: dbVersion, - Flags: []cli.Flag{&utils.ConfigFileFlag}, }, { Name: "migrate", Usage: "Migrate the database to the latest version.", Action: migrateDB, - Flags: []cli.Flag{&utils.ConfigFileFlag}, }, { Name: "rollback", Usage: "Roll back the database to a previous . Rolls back a single migration if no version specified.", Action: rollbackDB, Flags: []cli.Flag{ - &utils.ConfigFileFlag, &cli.IntFlag{ Name: "version", Usage: "Rollback to the specified version.", diff --git a/bridge-history-api/conf/config.json b/bridge-history-api/conf/config.json index 873bbbad5..a2a975bcd 100644 --- a/bridge-history-api/conf/config.json +++ b/bridge-history-api/conf/config.json @@ -18,7 +18,8 @@ "PufferGatewayAddr": "0xA033Ff09f2da45f0e9ae495f525363722Df42b2a", "ScrollChainAddr": "0xa13BAF47339d63B743e7Da8741db5456DAc1E556", "GatewayRouterAddr": "0xF8B1378579659D8F7EE5f3C929c2f3E332E41Fd6", - "MessageQueueAddr": "0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B" + "MessageQueueAddr": "0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B", + "BatchBridgeGatewayAddr": "0x0000000000000000000000000000000000000000" }, "L2": { "confirmation": 0, @@ -37,7 +38,8 @@ "DAIGatewayAddr": "0xaC78dff3A87b5b534e366A93E785a0ce8fA6Cc62", "PufferGatewayAddr": "0x9eBf2f33526CD571f8b2ad312492cb650870CFd6", "GatewayRouterAddr": "0x4C0926FF5252A435FD19e10ED15e5a249Ba19d79", - "MessageQueueAddr": "0x5300000000000000000000000000000000000000" + "MessageQueueAddr": "0x5300000000000000000000000000000000000000", + "BatchBridgeGatewayAddr": "0x0000000000000000000000000000000000000000" }, "db": { "dsn": "postgres://postgres:123456@localhost:5444/test?sslmode=disable", diff --git a/bridge-history-api/internal/config/config.go b/bridge-history-api/internal/config/config.go index d82bf8785..0005866b6 100644 --- a/bridge-history-api/internal/config/config.go +++ b/bridge-history-api/internal/config/config.go @@ -29,6 +29,7 @@ type FetcherConfig struct { ScrollChainAddr string `json:"ScrollChainAddr"` GatewayRouterAddr string `json:"GatewayRouterAddr"` MessageQueueAddr string `json:"MessageQueueAddr"` + BatchBridgeGatewayAddr string `json:"BatchBridgeGatewayAddr"` } // RedisConfig redis config diff --git a/bridge-history-api/internal/controller/api/controller.go b/bridge-history-api/internal/controller/api/controller.go index 26a68d5a0..7e4356fb3 100644 --- a/bridge-history-api/internal/controller/api/controller.go +++ b/bridge-history-api/internal/controller/api/controller.go @@ -8,8 +8,17 @@ import ( ) var ( - // HistoryCtrler is controller instance - HistoryCtrler *HistoryController + // TxsByAddressCtl the TxsByAddressController instance + TxsByAddressCtl *TxsByAddressController + + // TxsByHashesCtl the TxsByHashesController instance + TxsByHashesCtl *TxsByHashesController + + // L2UnclaimedWithdrawalsByAddressCtl the L2UnclaimedWithdrawalsByAddressController instance + L2UnclaimedWithdrawalsByAddressCtl *L2UnclaimedWithdrawalsByAddressController + + // L2WithdrawalsByAddressCtl the L2WithdrawalsByAddressController instance + L2WithdrawalsByAddressCtl *L2WithdrawalsByAddressController initControllerOnce sync.Once ) @@ -17,6 +26,9 @@ var ( // InitController inits Controller with database func InitController(db *gorm.DB, redis *redis.Client) { initControllerOnce.Do(func() { - HistoryCtrler = NewHistoryController(db, redis) + TxsByAddressCtl = NewTxsByAddressController(db, redis) + TxsByHashesCtl = NewTxsByHashesController(db, redis) + L2UnclaimedWithdrawalsByAddressCtl = NewL2UnclaimedWithdrawalsByAddressController(db, redis) + L2WithdrawalsByAddressCtl = NewL2WithdrawalsByAddressController(db, redis) }) } diff --git a/bridge-history-api/internal/controller/api/history_controller.go b/bridge-history-api/internal/controller/api/history_controller.go deleted file mode 100644 index 03d85d0cf..000000000 --- a/bridge-history-api/internal/controller/api/history_controller.go +++ /dev/null @@ -1,94 +0,0 @@ -package api - -import ( - "github.com/gin-gonic/gin" - "github.com/go-redis/redis/v8" - "gorm.io/gorm" - - "scroll-tech/bridge-history-api/internal/logic" - "scroll-tech/bridge-history-api/internal/types" -) - -// HistoryController contains the query claimable txs service -type HistoryController struct { - historyLogic *logic.HistoryLogic -} - -// NewHistoryController return HistoryController instance -func NewHistoryController(db *gorm.DB, redis *redis.Client) *HistoryController { - return &HistoryController{ - historyLogic: logic.NewHistoryLogic(db, redis), - } -} - -// GetL2UnclaimedWithdrawalsByAddress defines the http get method behavior -func (c *HistoryController) GetL2UnclaimedWithdrawalsByAddress(ctx *gin.Context) { - var req types.QueryByAddressRequest - if err := ctx.ShouldBind(&req); err != nil { - types.RenderFailure(ctx, types.ErrParameterInvalidNo, err) - return - } - - pagedTxs, total, err := c.historyLogic.GetL2UnclaimedWithdrawalsByAddress(ctx, req.Address, req.Page, req.PageSize) - if err != nil { - types.RenderFailure(ctx, types.ErrGetL2ClaimableWithdrawalsError, err) - return - } - - resultData := &types.ResultData{Results: pagedTxs, Total: total} - types.RenderSuccess(ctx, resultData) -} - -// GetL2WithdrawalsByAddress defines the http get method behavior -func (c *HistoryController) GetL2WithdrawalsByAddress(ctx *gin.Context) { - var req types.QueryByAddressRequest - if err := ctx.ShouldBind(&req); err != nil { - types.RenderFailure(ctx, types.ErrParameterInvalidNo, err) - return - } - - pagedTxs, total, err := c.historyLogic.GetL2WithdrawalsByAddress(ctx, req.Address, req.Page, req.PageSize) - if err != nil { - types.RenderFailure(ctx, types.ErrGetL2WithdrawalsError, err) - return - } - - resultData := &types.ResultData{Results: pagedTxs, Total: total} - types.RenderSuccess(ctx, resultData) -} - -// GetTxsByAddress defines the http get method behavior -func (c *HistoryController) GetTxsByAddress(ctx *gin.Context) { - var req types.QueryByAddressRequest - if err := ctx.ShouldBind(&req); err != nil { - types.RenderFailure(ctx, types.ErrParameterInvalidNo, err) - return - } - - pagedTxs, total, err := c.historyLogic.GetTxsByAddress(ctx, req.Address, req.Page, req.PageSize) - if err != nil { - types.RenderFailure(ctx, types.ErrGetTxsError, err) - return - } - - resultData := &types.ResultData{Results: pagedTxs, Total: total} - types.RenderSuccess(ctx, resultData) -} - -// PostQueryTxsByHashes defines the http post method behavior -func (c *HistoryController) PostQueryTxsByHashes(ctx *gin.Context) { - var req types.QueryByHashRequest - if err := ctx.ShouldBindJSON(&req); err != nil { - types.RenderFailure(ctx, types.ErrParameterInvalidNo, err) - return - } - - results, err := c.historyLogic.GetTxsByHashes(ctx, req.Txs) - if err != nil { - types.RenderFailure(ctx, types.ErrGetTxsByHashError, err) - return - } - - resultData := &types.ResultData{Results: results, Total: uint64(len(results))} - types.RenderSuccess(ctx, resultData) -} diff --git a/bridge-history-api/internal/controller/api/l2unclaimed_withdrawals_by_address.go b/bridge-history-api/internal/controller/api/l2unclaimed_withdrawals_by_address.go new file mode 100644 index 000000000..965e27b30 --- /dev/null +++ b/bridge-history-api/internal/controller/api/l2unclaimed_withdrawals_by_address.go @@ -0,0 +1,40 @@ +package api + +import ( + "github.com/gin-gonic/gin" + "github.com/go-redis/redis/v8" + "gorm.io/gorm" + + "scroll-tech/bridge-history-api/internal/logic" + "scroll-tech/bridge-history-api/internal/types" +) + +// L2UnclaimedWithdrawalsByAddressController the controller of GetL2UnclaimedWithdrawalsByAddress +type L2UnclaimedWithdrawalsByAddressController struct { + historyLogic *logic.HistoryLogic +} + +// NewL2UnclaimedWithdrawalsByAddressController create new L2UnclaimedWithdrawalsByAddressController +func NewL2UnclaimedWithdrawalsByAddressController(db *gorm.DB, redisClient *redis.Client) *L2UnclaimedWithdrawalsByAddressController { + return &L2UnclaimedWithdrawalsByAddressController{ + historyLogic: logic.NewHistoryLogic(db, redisClient), + } +} + +// GetL2UnclaimedWithdrawalsByAddress defines the http get method behavior +func (c *L2UnclaimedWithdrawalsByAddressController) GetL2UnclaimedWithdrawalsByAddress(ctx *gin.Context) { + var req types.QueryByAddressRequest + if err := ctx.ShouldBind(&req); err != nil { + types.RenderFailure(ctx, types.ErrParameterInvalidNo, err) + return + } + + pagedTxs, total, err := c.historyLogic.GetL2UnclaimedWithdrawalsByAddress(ctx, req.Address, req.Page, req.PageSize) + if err != nil { + types.RenderFailure(ctx, types.ErrGetL2ClaimableWithdrawalsError, err) + return + } + + resultData := &types.ResultData{Results: pagedTxs, Total: total} + types.RenderSuccess(ctx, resultData) +} diff --git a/bridge-history-api/internal/controller/api/l2withdrawals_by_address.go b/bridge-history-api/internal/controller/api/l2withdrawals_by_address.go new file mode 100644 index 000000000..2764ff4ea --- /dev/null +++ b/bridge-history-api/internal/controller/api/l2withdrawals_by_address.go @@ -0,0 +1,40 @@ +package api + +import ( + "github.com/gin-gonic/gin" + "github.com/go-redis/redis/v8" + "gorm.io/gorm" + + "scroll-tech/bridge-history-api/internal/logic" + "scroll-tech/bridge-history-api/internal/types" +) + +// L2WithdrawalsByAddressController the controller of GetL2WithdrawalsByAddress +type L2WithdrawalsByAddressController struct { + historyLogic *logic.HistoryLogic +} + +// NewL2WithdrawalsByAddressController create new L2WithdrawalsByAddressController +func NewL2WithdrawalsByAddressController(db *gorm.DB, redisClient *redis.Client) *L2WithdrawalsByAddressController { + return &L2WithdrawalsByAddressController{ + historyLogic: logic.NewHistoryLogic(db, redisClient), + } +} + +// GetL2WithdrawalsByAddress defines the http get method behavior +func (c *L2WithdrawalsByAddressController) GetL2WithdrawalsByAddress(ctx *gin.Context) { + var req types.QueryByAddressRequest + if err := ctx.ShouldBind(&req); err != nil { + types.RenderFailure(ctx, types.ErrParameterInvalidNo, err) + return + } + + pagedTxs, total, err := c.historyLogic.GetL2WithdrawalsByAddress(ctx, req.Address, req.Page, req.PageSize) + if err != nil { + types.RenderFailure(ctx, types.ErrGetL2WithdrawalsError, err) + return + } + + resultData := &types.ResultData{Results: pagedTxs, Total: total} + types.RenderSuccess(ctx, resultData) +} diff --git a/bridge-history-api/internal/controller/api/txs_by_address.go b/bridge-history-api/internal/controller/api/txs_by_address.go new file mode 100644 index 000000000..0918dbfa0 --- /dev/null +++ b/bridge-history-api/internal/controller/api/txs_by_address.go @@ -0,0 +1,40 @@ +package api + +import ( + "github.com/gin-gonic/gin" + "github.com/go-redis/redis/v8" + "gorm.io/gorm" + + "scroll-tech/bridge-history-api/internal/logic" + "scroll-tech/bridge-history-api/internal/types" +) + +// TxsByAddressController the controller of GetTxsByAddress +type TxsByAddressController struct { + historyLogic *logic.HistoryLogic +} + +// NewTxsByAddressController create new TxsByAddressController +func NewTxsByAddressController(db *gorm.DB, redisClient *redis.Client) *TxsByAddressController { + return &TxsByAddressController{ + historyLogic: logic.NewHistoryLogic(db, redisClient), + } +} + +// GetTxsByAddress defines the http get method behavior +func (c *TxsByAddressController) GetTxsByAddress(ctx *gin.Context) { + var req types.QueryByAddressRequest + if err := ctx.ShouldBind(&req); err != nil { + types.RenderFailure(ctx, types.ErrParameterInvalidNo, err) + return + } + + pagedTxs, total, err := c.historyLogic.GetTxsByAddress(ctx, req.Address, req.Page, req.PageSize) + if err != nil { + types.RenderFailure(ctx, types.ErrGetTxsError, err) + return + } + + resultData := &types.ResultData{Results: pagedTxs, Total: total} + types.RenderSuccess(ctx, resultData) +} diff --git a/bridge-history-api/internal/controller/api/txs_by_hashes.go b/bridge-history-api/internal/controller/api/txs_by_hashes.go new file mode 100644 index 000000000..98440036a --- /dev/null +++ b/bridge-history-api/internal/controller/api/txs_by_hashes.go @@ -0,0 +1,40 @@ +package api + +import ( + "github.com/gin-gonic/gin" + "github.com/go-redis/redis/v8" + "gorm.io/gorm" + + "scroll-tech/bridge-history-api/internal/logic" + "scroll-tech/bridge-history-api/internal/types" +) + +// TxsByHashesController the controller of PostQueryTxsByHashes +type TxsByHashesController struct { + historyLogic *logic.HistoryLogic +} + +// NewTxsByHashesController create a new TxsByHashesController +func NewTxsByHashesController(db *gorm.DB, redisClient *redis.Client) *TxsByHashesController { + return &TxsByHashesController{ + historyLogic: logic.NewHistoryLogic(db, redisClient), + } +} + +// PostQueryTxsByHashes query the txs by hashes +func (c *TxsByHashesController) PostQueryTxsByHashes(ctx *gin.Context) { + var req types.QueryByHashRequest + if err := ctx.ShouldBindJSON(&req); err != nil { + types.RenderFailure(ctx, types.ErrParameterInvalidNo, err) + return + } + + results, err := c.historyLogic.GetTxsByHashes(ctx, req.Txs) + if err != nil { + types.RenderFailure(ctx, types.ErrGetTxsByHashError, err) + return + } + + resultData := &types.ResultData{Results: results, Total: uint64(len(results))} + types.RenderSuccess(ctx, resultData) +} diff --git a/bridge-history-api/internal/controller/fetcher/l1_fetcher.go b/bridge-history-api/internal/controller/fetcher/l1_fetcher.go index bc92e1147..e71821367 100644 --- a/bridge-history-api/internal/controller/fetcher/l1_fetcher.go +++ b/bridge-history-api/internal/controller/fetcher/l1_fetcher.go @@ -63,7 +63,7 @@ func NewL1MessageFetcher(ctx context.Context, cfg *config.FetcherConfig, db *gor // Start starts the L1 message fetching process. func (c *L1MessageFetcher) Start() { - messageSyncedHeight, batchSyncedHeight, dbErr := c.eventUpdateLogic.GetL1SyncHeight(c.ctx) + messageSyncedHeight, batchSyncedHeight, bridgeBatchDepositSyncedHeight, dbErr := c.eventUpdateLogic.GetL1SyncHeight(c.ctx) if dbErr != nil { log.Crit("L1MessageFetcher start failed", "err", dbErr) } @@ -72,6 +72,11 @@ func (c *L1MessageFetcher) Start() { if batchSyncedHeight > l1SyncHeight { l1SyncHeight = batchSyncedHeight } + + if bridgeBatchDepositSyncedHeight > l1SyncHeight { + l1SyncHeight = bridgeBatchDepositSyncedHeight + } + if c.cfg.StartHeight > l1SyncHeight { l1SyncHeight = c.cfg.StartHeight - 1 } @@ -91,7 +96,13 @@ func (c *L1MessageFetcher) Start() { c.updateL1SyncHeight(l1SyncHeight, header.Hash()) - log.Info("Start L1 message fetcher", "message synced height", messageSyncedHeight, "batch synced height", batchSyncedHeight, "config start height", c.cfg.StartHeight, "sync start height", c.l1SyncHeight+1) + log.Info("Start L1 message fetcher", + "message synced height", messageSyncedHeight, + "batch synced height", batchSyncedHeight, + "bridge batch deposit height", bridgeBatchDepositSyncedHeight, + "config start height", c.cfg.StartHeight, + "sync start height", c.l1SyncHeight+1, + ) tick := time.NewTicker(time.Duration(c.cfg.BlockTime) * time.Second) go func() { diff --git a/bridge-history-api/internal/controller/fetcher/l2_fetcher.go b/bridge-history-api/internal/controller/fetcher/l2_fetcher.go index 1497ee2c7..03e909247 100644 --- a/bridge-history-api/internal/controller/fetcher/l2_fetcher.go +++ b/bridge-history-api/internal/controller/fetcher/l2_fetcher.go @@ -64,13 +64,17 @@ func NewL2MessageFetcher(ctx context.Context, cfg *config.FetcherConfig, db *gor // Start starts the L2 message fetching process. func (c *L2MessageFetcher) Start() { - l2SentMessageSyncedHeight, dbErr := c.eventUpdateLogic.GetL2MessageSyncedHeightInDB(c.ctx) + l2SentMessageSyncedHeight, l2BridgeBatchDepositSyncedHeight, dbErr := c.eventUpdateLogic.GetL2MessageSyncedHeightInDB(c.ctx) if dbErr != nil { log.Crit("failed to get L2 cross message processed height", "err", dbErr) return } l2SyncHeight := l2SentMessageSyncedHeight + if l2BridgeBatchDepositSyncedHeight > l2SyncHeight { + l2SyncHeight = l2BridgeBatchDepositSyncedHeight + } + // Sync from an older block to prevent reorg during restart. if l2SyncHeight < logic.L2ReorgSafeDepth { l2SyncHeight = 0 @@ -86,7 +90,8 @@ func (c *L2MessageFetcher) Start() { c.updateL2SyncHeight(l2SyncHeight, header.Hash()) - log.Info("Start L2 message fetcher", "message synced height", l2SentMessageSyncedHeight, "sync start height", l2SyncHeight+1) + log.Info("Start L2 message fetcher", "l2 sent message synced height", l2SentMessageSyncedHeight, + "bridge batch deposit synced height", l2BridgeBatchDepositSyncedHeight, "sync start height", l2SyncHeight+1) tick := time.NewTicker(time.Duration(c.cfg.BlockTime) * time.Second) go func() { @@ -141,6 +146,11 @@ func (c *L2MessageFetcher) fetchAndSaveEvents(confirmation uint64) { return } + if updateErr := c.eventUpdateLogic.UpdateL2BridgeBatchDepositEvent(c.ctx, l2FetcherResult.BridgeBatchDepositMessage); updateErr != nil { + log.Error("failed to update L1 batch index and status", "from", from, "to", to, "err", updateErr) + return + } + c.updateL2SyncHeight(to, lastBlockHash) c.l2MessageFetcherRunningTotal.Inc() } diff --git a/bridge-history-api/internal/logic/event_update.go b/bridge-history-api/internal/logic/event_update.go index a5229feda..5255ccc9e 100644 --- a/bridge-history-api/internal/logic/event_update.go +++ b/bridge-history-api/internal/logic/event_update.go @@ -11,14 +11,16 @@ import ( "gorm.io/gorm" "scroll-tech/bridge-history-api/internal/orm" + btypes "scroll-tech/bridge-history-api/internal/types" "scroll-tech/bridge-history-api/internal/utils" ) // EventUpdateLogic the logic of insert/update the database type EventUpdateLogic struct { - db *gorm.DB - crossMessageOrm *orm.CrossMessage - batchEventOrm *orm.BatchEvent + db *gorm.DB + crossMessageOrm *orm.CrossMessage + batchEventOrm *orm.BatchEvent + bridgeBatchDepositEventOrm *orm.BridgeBatchDepositEvent eventUpdateLogicL1FinalizeBatchEventL2BlockUpdateHeight prometheus.Gauge eventUpdateLogicL2MessageNonceUpdateHeight prometheus.Gauge @@ -27,9 +29,10 @@ type EventUpdateLogic struct { // NewEventUpdateLogic creates a EventUpdateLogic instance func NewEventUpdateLogic(db *gorm.DB, isL1 bool) *EventUpdateLogic { b := &EventUpdateLogic{ - db: db, - crossMessageOrm: orm.NewCrossMessage(db), - batchEventOrm: orm.NewBatchEvent(db), + db: db, + crossMessageOrm: orm.NewCrossMessage(db), + batchEventOrm: orm.NewBatchEvent(db), + bridgeBatchDepositEventOrm: orm.NewBridgeBatchDepositEvent(db), } if !isL1 { @@ -48,30 +51,42 @@ func NewEventUpdateLogic(db *gorm.DB, isL1 bool) *EventUpdateLogic { } // GetL1SyncHeight gets the l1 sync height from db -func (b *EventUpdateLogic) GetL1SyncHeight(ctx context.Context) (uint64, uint64, error) { - messageSyncedHeight, err := b.crossMessageOrm.GetMessageSyncedHeightInDB(ctx, orm.MessageTypeL1SentMessage) +func (b *EventUpdateLogic) GetL1SyncHeight(ctx context.Context) (uint64, uint64, uint64, error) { + messageSyncedHeight, err := b.crossMessageOrm.GetMessageSyncedHeightInDB(ctx, btypes.MessageTypeL1SentMessage) if err != nil { log.Error("failed to get L1 cross message synced height", "error", err) - return 0, 0, err + return 0, 0, 0, err } batchSyncedHeight, err := b.batchEventOrm.GetBatchEventSyncedHeightInDB(ctx) if err != nil { log.Error("failed to get L1 batch event synced height", "error", err) - return 0, 0, err + return 0, 0, 0, err } - return messageSyncedHeight, batchSyncedHeight, nil + bridgeBatchDepositSyncedHeight, err := b.bridgeBatchDepositEventOrm.GetMessageL1SyncedHeightInDB(ctx) + if err != nil { + log.Error("failed to get l1 bridge batch deposit synced height", "error", err) + return 0, 0, 0, err + } + + return messageSyncedHeight, batchSyncedHeight, bridgeBatchDepositSyncedHeight, nil } // GetL2MessageSyncedHeightInDB gets L2 messages synced height -func (b *EventUpdateLogic) GetL2MessageSyncedHeightInDB(ctx context.Context) (uint64, error) { - l2SentMessageSyncedHeight, err := b.crossMessageOrm.GetMessageSyncedHeightInDB(ctx, orm.MessageTypeL2SentMessage) +func (b *EventUpdateLogic) GetL2MessageSyncedHeightInDB(ctx context.Context) (uint64, uint64, error) { + l2SentMessageSyncedHeight, err := b.crossMessageOrm.GetMessageSyncedHeightInDB(ctx, btypes.MessageTypeL2SentMessage) if err != nil { log.Error("failed to get L2 cross message processed height", "err", err) - return 0, err + return 0, 0, err } - return l2SentMessageSyncedHeight, nil + + l2BridgeBatchDepositSyncHeight, err := b.bridgeBatchDepositEventOrm.GetMessageL2SyncedHeightInDB(ctx) + if err != nil { + log.Error("failed to get bridge batch deposit processed height", "err", err) + return 0, 0, err + } + return l2SentMessageSyncedHeight, l2BridgeBatchDepositSyncHeight, nil } // L1InsertOrUpdate inserts or updates l1 messages @@ -100,6 +115,12 @@ func (b *EventUpdateLogic) L1InsertOrUpdate(ctx context.Context, l1FetcherResult log.Error("failed to insert failed L1 gateway transactions", "err", err) return err } + + if err := b.bridgeBatchDepositEventOrm.InsertOrUpdateL1BridgeBatchDepositEvent(ctx, l1FetcherResult.BridgeBatchDepositEvents); err != nil { + log.Error("failed to insert L1 bridge batch deposit transactions", "err", err) + return err + } + return nil } @@ -139,7 +160,7 @@ func (b *EventUpdateLogic) updateL2WithdrawMessageInfos(ctx context.Context, bat for i, message := range l2WithdrawMessages { message.MerkleProof = proofs[i] - message.RollupStatus = int(orm.RollupStatusTypeFinalized) + message.RollupStatus = int(btypes.RollupStatusTypeFinalized) message.BatchIndex = batchIndex } @@ -175,6 +196,30 @@ func (b *EventUpdateLogic) UpdateL1BatchIndexAndStatus(ctx context.Context, heig return nil } +// UpdateL2BridgeBatchDepositEvent update l2 bridge batch deposit status +func (b *EventUpdateLogic) UpdateL2BridgeBatchDepositEvent(ctx context.Context, l2BatchDistributes []*orm.BridgeBatchDepositEvent) error { + distributeFailedMap := make(map[uint64][]string) + for _, l2BatchDistribute := range l2BatchDistributes { + if btypes.TxStatusType(l2BatchDistribute.TxStatus) == btypes.TxStatusBridgeBatchDistributeFailed { + distributeFailedMap[l2BatchDistribute.BatchIndex] = append(distributeFailedMap[l2BatchDistribute.BatchIndex], l2BatchDistribute.Sender) + } + + if err := b.bridgeBatchDepositEventOrm.UpdateBatchEventStatus(ctx, l2BatchDistribute); err != nil { + log.Error("failed to update L1 bridge batch distribute event", "batchIndex", l2BatchDistribute.BatchIndex, "err", err) + return err + } + } + + for batchIndex, distributeFailedSenders := range distributeFailedMap { + if err := b.bridgeBatchDepositEventOrm.UpdateDistributeFailedStatus(ctx, batchIndex, distributeFailedSenders); err != nil { + log.Error("failed to update L1 bridge batch distribute failed event", "batchIndex", batchIndex, "failed senders", distributeFailedSenders, "err", err) + return err + } + } + + return nil +} + // L2InsertOrUpdate inserts or updates L2 messages func (b *EventUpdateLogic) L2InsertOrUpdate(ctx context.Context, l2FetcherResult *L2FilterResult) error { if err := b.crossMessageOrm.InsertOrUpdateL2Messages(ctx, l2FetcherResult.WithdrawMessages); err != nil { diff --git a/bridge-history-api/internal/logic/history_logic.go b/bridge-history-api/internal/logic/history_logic.go index 5e9b83070..0d97b6f0a 100644 --- a/bridge-history-api/internal/logic/history_logic.go +++ b/bridge-history-api/internal/logic/history_logic.go @@ -16,6 +16,7 @@ import ( "scroll-tech/bridge-history-api/internal/orm" "scroll-tech/bridge-history-api/internal/types" + btypes "scroll-tech/bridge-history-api/internal/types" "scroll-tech/bridge-history-api/internal/utils" ) @@ -35,20 +36,23 @@ const ( // HistoryLogic services. type HistoryLogic struct { - crossMessageOrm *orm.CrossMessage - batchEventOrm *orm.BatchEvent - redis *redis.Client - singleFlight singleflight.Group - cacheMetrics *cacheMetrics + crossMessageOrm *orm.CrossMessage + batchEventOrm *orm.BatchEvent + bridgeBatchDepositOrm *orm.BridgeBatchDepositEvent + + redis *redis.Client + singleFlight singleflight.Group + cacheMetrics *cacheMetrics } // NewHistoryLogic returns bridge history services. func NewHistoryLogic(db *gorm.DB, redis *redis.Client) *HistoryLogic { logic := &HistoryLogic{ - crossMessageOrm: orm.NewCrossMessage(db), - batchEventOrm: orm.NewBatchEvent(db), - redis: redis, - cacheMetrics: initCacheMetrics(), + crossMessageOrm: orm.NewCrossMessage(db), + batchEventOrm: orm.NewBatchEvent(db), + bridgeBatchDepositOrm: orm.NewBridgeBatchDepositEvent(db), + redis: redis, + cacheMetrics: initCacheMetrics(), } return logic } @@ -72,25 +76,28 @@ func (h *HistoryLogic) GetL2UnclaimedWithdrawalsByAddress(ctx context.Context, a log.Info("cache miss", "cache key", cacheKey) result, err, _ := h.singleFlight.Do(cacheKey, func() (interface{}, error) { - var messages []*orm.CrossMessage - messages, err = h.crossMessageOrm.GetL2UnclaimedWithdrawalsByAddress(ctx, address) - if err != nil { - return nil, err + var txHistoryInfos []*types.TxHistoryInfo + crossMessages, getErr := h.crossMessageOrm.GetL2UnclaimedWithdrawalsByAddress(ctx, address) + if getErr != nil { + return nil, getErr } - return messages, nil + for _, message := range crossMessages { + txHistoryInfos = append(txHistoryInfos, getTxHistoryInfoFromCrossMessage(message)) + } + return txHistoryInfos, nil }) if err != nil { log.Error("failed to get L2 claimable withdrawals by address", "address", address, "error", err) return nil, 0, err } - messages, ok := result.([]*orm.CrossMessage) + txHistoryInfos, ok := result.([]*types.TxHistoryInfo) if !ok { log.Error("unexpected type", "expected", "[]*types.TxHistoryInfo", "got", reflect.TypeOf(result), "address", address) return nil, 0, errors.New("unexpected error") } - return h.processAndCacheTxHistoryInfo(ctx, cacheKey, messages, page, pageSize) + return h.processAndCacheTxHistoryInfo(ctx, cacheKey, txHistoryInfos, page, pageSize) } // GetL2WithdrawalsByAddress gets all withdrawal txs under given address. @@ -112,25 +119,28 @@ func (h *HistoryLogic) GetL2WithdrawalsByAddress(ctx context.Context, address st log.Info("cache miss", "cache key", cacheKey) result, err, _ := h.singleFlight.Do(cacheKey, func() (interface{}, error) { - var messages []*orm.CrossMessage - messages, err = h.crossMessageOrm.GetL2WithdrawalsByAddress(ctx, address) - if err != nil { - return nil, err + var txHistoryInfos []*types.TxHistoryInfo + crossMessages, getErr := h.crossMessageOrm.GetL2WithdrawalsByAddress(ctx, address) + if getErr != nil { + return nil, getErr } - return messages, nil + for _, message := range crossMessages { + txHistoryInfos = append(txHistoryInfos, getTxHistoryInfoFromCrossMessage(message)) + } + return txHistoryInfos, nil }) if err != nil { log.Error("failed to get L2 withdrawals by address", "address", address, "error", err) return nil, 0, err } - messages, ok := result.([]*orm.CrossMessage) + txHistoryInfos, ok := result.([]*types.TxHistoryInfo) if !ok { log.Error("unexpected type", "expected", "[]*types.TxHistoryInfo", "got", reflect.TypeOf(result), "address", address) return nil, 0, errors.New("unexpected error") } - return h.processAndCacheTxHistoryInfo(ctx, cacheKey, messages, page, pageSize) + return h.processAndCacheTxHistoryInfo(ctx, cacheKey, txHistoryInfos, page, pageSize) } // GetTxsByAddress gets tx infos under given address. @@ -152,25 +162,36 @@ func (h *HistoryLogic) GetTxsByAddress(ctx context.Context, address string, page log.Info("cache miss", "cache key", cacheKey) result, err, _ := h.singleFlight.Do(cacheKey, func() (interface{}, error) { - var messages []*orm.CrossMessage - messages, err = h.crossMessageOrm.GetTxsByAddress(ctx, address) - if err != nil { - return nil, err + var txHistoryInfos []*types.TxHistoryInfo + crossMessages, getErr := h.crossMessageOrm.GetTxsByAddress(ctx, address) + if getErr != nil { + return nil, getErr } - return messages, nil + for _, message := range crossMessages { + txHistoryInfos = append(txHistoryInfos, getTxHistoryInfoFromCrossMessage(message)) + } + + batchDepositMessages, getErr := h.bridgeBatchDepositOrm.GetTxsByAddress(ctx, address) + if getErr != nil { + return nil, getErr + } + for _, message := range batchDepositMessages { + txHistoryInfos = append(txHistoryInfos, getTxHistoryInfoFromBridgeBatchDepositMessage(message)) + } + return txHistoryInfos, nil }) if err != nil { log.Error("failed to get txs by address", "address", address, "error", err) return nil, 0, err } - messages, ok := result.([]*orm.CrossMessage) + txHistoryInfos, ok := result.([]*types.TxHistoryInfo) if !ok { log.Error("unexpected type", "expected", "[]*types.TxHistoryInfo", "got", reflect.TypeOf(result), "address", address) return nil, 0, errors.New("unexpected error") } - return h.processAndCacheTxHistoryInfo(ctx, cacheKey, messages, page, pageSize) + return h.processAndCacheTxHistoryInfo(ctx, cacheKey, txHistoryInfos, page, pageSize) } // GetTxsByHashes gets tx infos under given tx hashes. @@ -218,15 +239,24 @@ func (h *HistoryLogic) GetTxsByHashes(ctx context.Context, txHashes []string) ([ } if len(uncachedHashes) > 0 { - messages, err := h.crossMessageOrm.GetMessagesByTxHashes(ctx, uncachedHashes) + var txHistories []*types.TxHistoryInfo + + crossMessages, err := h.crossMessageOrm.GetMessagesByTxHashes(ctx, uncachedHashes) if err != nil { - log.Error("failed to get messages by tx hashes", "hashes", uncachedHashes) + log.Error("failed to get cross messages by tx hashes", "hashes", uncachedHashes) return nil, err } + for _, message := range crossMessages { + txHistories = append(txHistories, getTxHistoryInfoFromCrossMessage(message)) + } - var txHistories []*types.TxHistoryInfo - for _, message := range messages { - txHistories = append(txHistories, getTxHistoryInfo(message)) + batchDepositMessages, err := h.bridgeBatchDepositOrm.GetMessagesByTxHashes(ctx, uncachedHashes) + if err != nil { + log.Error("failed to get batch deposit messages by tx hashes", "hashes", uncachedHashes) + return nil, err + } + for _, message := range batchDepositMessages { + txHistories = append(txHistories, getTxHistoryInfoFromBridgeBatchDepositMessage(message)) } resultMap := make(map[string]*types.TxHistoryInfo) @@ -260,19 +290,19 @@ func (h *HistoryLogic) GetTxsByHashes(ctx context.Context, txHashes []string) ([ return results, nil } -func getTxHistoryInfo(message *orm.CrossMessage) *types.TxHistoryInfo { +func getTxHistoryInfoFromCrossMessage(message *orm.CrossMessage) *types.TxHistoryInfo { txHistory := &types.TxHistoryInfo{ MessageHash: message.MessageHash, - TokenType: orm.TokenType(message.TokenType), + TokenType: btypes.TokenType(message.TokenType), TokenIDs: utils.ConvertStringToStringArray(message.TokenIDs), TokenAmounts: utils.ConvertStringToStringArray(message.TokenAmounts), L1TokenAddress: message.L1TokenAddress, L2TokenAddress: message.L2TokenAddress, - MessageType: orm.MessageType(message.MessageType), - TxStatus: orm.TxStatusType(message.TxStatus), + MessageType: btypes.MessageType(message.MessageType), + TxStatus: btypes.TxStatusType(message.TxStatus), BlockTimestamp: message.BlockTimestamp, } - if txHistory.MessageType == orm.MessageTypeL1SentMessage { + if txHistory.MessageType == btypes.MessageTypeL1SentMessage { txHistory.Hash = message.L1TxHash txHistory.ReplayTxHash = message.L1ReplayTxHash txHistory.RefundTxHash = message.L1RefundTxHash @@ -288,7 +318,7 @@ func getTxHistoryInfo(message *orm.CrossMessage) *types.TxHistoryInfo { Hash: message.L1TxHash, BlockNumber: message.L1BlockNumber, } - if orm.RollupStatusType(message.RollupStatus) == orm.RollupStatusTypeFinalized { + if btypes.RollupStatusType(message.RollupStatus) == btypes.RollupStatusTypeFinalized { txHistory.ClaimInfo = &types.ClaimInfo{ From: message.MessageFrom, To: message.MessageTo, @@ -306,6 +336,28 @@ func getTxHistoryInfo(message *orm.CrossMessage) *types.TxHistoryInfo { return txHistory } +func getTxHistoryInfoFromBridgeBatchDepositMessage(message *orm.BridgeBatchDepositEvent) *types.TxHistoryInfo { + txHistory := &types.TxHistoryInfo{ + Hash: message.L1TxHash, + TokenType: btypes.TokenType(message.TokenType), + TokenAmounts: utils.ConvertStringToStringArray(message.TokenAmount), + BlockNumber: message.L1BlockNumber, + MessageType: btypes.MessageTypeL1BatchDeposit, + TxStatus: btypes.TxStatusType(message.TxStatus), + CounterpartChainTx: &types.CounterpartChainTx{ + Hash: message.L2TxHash, + BlockNumber: message.L2BlockNumber, + }, + BlockTimestamp: message.BlockTimestamp, + BatchDepositFee: message.Fee, + } + if txHistory.TokenType != btypes.TokenTypeETH { + txHistory.L1TokenAddress = message.L1TokenAddress + txHistory.L2TokenAddress = message.L2TokenAddress + } + return txHistory +} + func (h *HistoryLogic) getCachedTxsInfo(ctx context.Context, cacheKey string, pageNum, pageSize uint64) ([]*types.TxHistoryInfo, uint64, bool, error) { start := int64((pageNum - 1) * pageSize) end := start + int64(pageSize) - 1 @@ -320,7 +372,7 @@ func (h *HistoryLogic) getCachedTxsInfo(ctx context.Context, cacheKey string, pa return nil, 0, false, nil } - values, err := h.redis.ZRange(ctx, cacheKey, start, end).Result() + values, err := h.redis.ZRevRange(ctx, cacheKey, start, end).Result() if err != nil { log.Error("failed to get zrange result", "error", err) return nil, 0, false, err @@ -356,13 +408,13 @@ func (h *HistoryLogic) cacheTxsInfo(ctx context.Context, cacheKey string, txs [] } } else { // The transactions are sorted, thus we set the score as their indices. - for i, tx := range txs { + for _, tx := range txs { txBytes, err := json.Marshal(tx) if err != nil { log.Error("failed to marshal transaction to json", "error", err) return err } - if err := pipe.ZAdd(ctx, cacheKey, &redis.Z{Score: float64(i), Member: txBytes}).Err(); err != nil { + if err := pipe.ZAdd(ctx, cacheKey, &redis.Z{Score: float64(tx.BlockTimestamp), Member: txBytes}).Err(); err != nil { log.Error("failed to add transaction to sorted set", "error", err) return err } @@ -381,12 +433,7 @@ func (h *HistoryLogic) cacheTxsInfo(ctx context.Context, cacheKey string, txs [] return nil } -func (h *HistoryLogic) processAndCacheTxHistoryInfo(ctx context.Context, cacheKey string, messages []*orm.CrossMessage, page, pageSize uint64) ([]*types.TxHistoryInfo, uint64, error) { - var txHistories []*types.TxHistoryInfo - for _, message := range messages { - txHistories = append(txHistories, getTxHistoryInfo(message)) - } - +func (h *HistoryLogic) processAndCacheTxHistoryInfo(ctx context.Context, cacheKey string, txHistories []*types.TxHistoryInfo, page, pageSize uint64) ([]*types.TxHistoryInfo, uint64, error) { err := h.cacheTxsInfo(ctx, cacheKey, txHistories) if err != nil { log.Error("failed to cache txs info", "key", cacheKey, "err", err) diff --git a/bridge-history-api/internal/logic/l1_event_parser.go b/bridge-history-api/internal/logic/l1_event_parser.go index 5432c3b3b..837ae3765 100644 --- a/bridge-history-api/internal/logic/l1_event_parser.go +++ b/bridge-history-api/internal/logic/l1_event_parser.go @@ -13,6 +13,7 @@ import ( backendabi "scroll-tech/bridge-history-api/abi" "scroll-tech/bridge-history-api/internal/config" "scroll-tech/bridge-history-api/internal/orm" + btypes "scroll-tech/bridge-history-api/internal/types" "scroll-tech/bridge-history-api/internal/utils" ) @@ -30,8 +31,60 @@ func NewL1EventParser(cfg *config.FetcherConfig, client *ethclient.Client) *L1Ev } } -// ParseL1CrossChainEventLogs parses L1 watched cross chain events. -func (e *L1EventParser) ParseL1CrossChainEventLogs(ctx context.Context, logs []types.Log, blockTimestampsMap map[uint64]uint64) ([]*orm.CrossMessage, []*orm.CrossMessage, error) { +// ParseL1CrossChainEventLogs parse l1 cross chain event logs +func (e *L1EventParser) ParseL1CrossChainEventLogs(ctx context.Context, logs []types.Log, blockTimestampsMap map[uint64]uint64) ([]*orm.CrossMessage, []*orm.CrossMessage, []*orm.BridgeBatchDepositEvent, error) { + l1CrossChainDepositMessages, l1CrossChainRelayedMessages, err := e.ParseL1SingleCrossChainEventLogs(ctx, logs, blockTimestampsMap) + if err != nil { + return nil, nil, nil, err + } + + l1BridgeBatchDepositMessages, err := e.ParseL1BridgeBatchDepositCrossChainEventLogs(logs, blockTimestampsMap) + if err != nil { + return nil, nil, nil, err + } + + return l1CrossChainDepositMessages, l1CrossChainRelayedMessages, l1BridgeBatchDepositMessages, nil +} + +// ParseL1BridgeBatchDepositCrossChainEventLogs parse L1 watched batch bridge cross chain events. +func (e *L1EventParser) ParseL1BridgeBatchDepositCrossChainEventLogs(logs []types.Log, blockTimestampsMap map[uint64]uint64) ([]*orm.BridgeBatchDepositEvent, error) { + var l1BridgeBatchDepositMessages []*orm.BridgeBatchDepositEvent + for _, vlog := range logs { + switch vlog.Topics[0] { + case backendabi.L1BridgeBatchDepositSig: + event := backendabi.L1BatchBridgeGatewayDeposit{} + if err := utils.UnpackLog(backendabi.L1BatchBridgeGatewayABI, &event, "Deposit", vlog); err != nil { + log.Error("Failed to unpack batch bridge gateway deposit event", "err", err) + return nil, err + } + + var tokenType btypes.TokenType + if event.Token == common.HexToAddress("0") { + tokenType = btypes.TokenTypeETH + } else { + tokenType = btypes.TokenTypeERC20 + } + + l1BridgeBatchDepositMessages = append(l1BridgeBatchDepositMessages, &orm.BridgeBatchDepositEvent{ + TokenType: int(tokenType), + Sender: event.Sender.String(), + BatchIndex: event.BatchIndex.Uint64(), + TokenAmount: event.Amount.String(), + Fee: event.Fee.String(), + L1TokenAddress: event.Token.String(), + L1BlockNumber: vlog.BlockNumber, + L1TxHash: vlog.TxHash.String(), + TxStatus: int(btypes.TxStatusBridgeBatchDeposit), + BlockTimestamp: blockTimestampsMap[vlog.BlockNumber], + L1LogIndex: vlog.Index, + }) + } + } + return l1BridgeBatchDepositMessages, nil +} + +// ParseL1SingleCrossChainEventLogs parses L1 watched single cross chain events. +func (e *L1EventParser) ParseL1SingleCrossChainEventLogs(ctx context.Context, logs []types.Log, blockTimestampsMap map[uint64]uint64) ([]*orm.CrossMessage, []*orm.CrossMessage, error) { var l1DepositMessages []*orm.CrossMessage var l1RelayedMessages []*orm.CrossMessage for _, vlog := range logs { @@ -45,7 +98,7 @@ func (e *L1EventParser) ParseL1CrossChainEventLogs(ctx context.Context, logs []t lastMessage := l1DepositMessages[len(l1DepositMessages)-1] lastMessage.Sender = event.From.String() lastMessage.Receiver = event.To.String() - lastMessage.TokenType = int(orm.TokenTypeETH) + lastMessage.TokenType = int(btypes.TokenTypeETH) lastMessage.TokenAmounts = event.Amount.String() case backendabi.L1DepositERC20Sig: event := backendabi.ERC20MessageEvent{} @@ -57,7 +110,7 @@ func (e *L1EventParser) ParseL1CrossChainEventLogs(ctx context.Context, logs []t lastMessage := l1DepositMessages[len(l1DepositMessages)-1] lastMessage.Sender = event.From.String() lastMessage.Receiver = event.To.String() - lastMessage.TokenType = int(orm.TokenTypeERC20) + lastMessage.TokenType = int(btypes.TokenTypeERC20) lastMessage.L1TokenAddress = event.L1Token.String() lastMessage.L2TokenAddress = event.L2Token.String() lastMessage.TokenAmounts = event.Amount.String() @@ -70,7 +123,7 @@ func (e *L1EventParser) ParseL1CrossChainEventLogs(ctx context.Context, logs []t lastMessage := l1DepositMessages[len(l1DepositMessages)-1] lastMessage.Sender = event.From.String() lastMessage.Receiver = event.To.String() - lastMessage.TokenType = int(orm.TokenTypeERC721) + lastMessage.TokenType = int(btypes.TokenTypeERC721) lastMessage.L1TokenAddress = event.L1Token.String() lastMessage.L2TokenAddress = event.L2Token.String() lastMessage.TokenIDs = event.TokenID.String() @@ -83,7 +136,7 @@ func (e *L1EventParser) ParseL1CrossChainEventLogs(ctx context.Context, logs []t lastMessage := l1DepositMessages[len(l1DepositMessages)-1] lastMessage.Sender = event.From.String() lastMessage.Receiver = event.To.String() - lastMessage.TokenType = int(orm.TokenTypeERC721) + lastMessage.TokenType = int(btypes.TokenTypeERC721) lastMessage.L1TokenAddress = event.L1Token.String() lastMessage.L2TokenAddress = event.L2Token.String() lastMessage.TokenIDs = utils.ConvertBigIntArrayToString(event.TokenIDs) @@ -96,7 +149,7 @@ func (e *L1EventParser) ParseL1CrossChainEventLogs(ctx context.Context, logs []t lastMessage := l1DepositMessages[len(l1DepositMessages)-1] lastMessage.Sender = event.From.String() lastMessage.Receiver = event.To.String() - lastMessage.TokenType = int(orm.TokenTypeERC1155) + lastMessage.TokenType = int(btypes.TokenTypeERC1155) lastMessage.L1TokenAddress = event.L1Token.String() lastMessage.L2TokenAddress = event.L2Token.String() lastMessage.TokenIDs = event.TokenID.String() @@ -110,7 +163,7 @@ func (e *L1EventParser) ParseL1CrossChainEventLogs(ctx context.Context, logs []t lastMessage := l1DepositMessages[len(l1DepositMessages)-1] lastMessage.Sender = event.From.String() lastMessage.Receiver = event.To.String() - lastMessage.TokenType = int(orm.TokenTypeERC1155) + lastMessage.TokenType = int(btypes.TokenTypeERC1155) lastMessage.L1TokenAddress = event.L1Token.String() lastMessage.L2TokenAddress = event.L2Token.String() lastMessage.TokenIDs = utils.ConvertBigIntArrayToString(event.TokenIDs) @@ -130,12 +183,12 @@ func (e *L1EventParser) ParseL1CrossChainEventLogs(ctx context.Context, logs []t L1BlockNumber: vlog.BlockNumber, Sender: from, Receiver: event.Target.String(), - TokenType: int(orm.TokenTypeETH), + TokenType: int(btypes.TokenTypeETH), L1TxHash: vlog.TxHash.String(), TokenAmounts: event.Value.String(), MessageNonce: event.MessageNonce.Uint64(), - MessageType: int(orm.MessageTypeL1SentMessage), - TxStatus: int(orm.TxStatusTypeSent), + MessageType: int(btypes.MessageTypeL1SentMessage), + TxStatus: int(btypes.TxStatusTypeSent), BlockTimestamp: blockTimestampsMap[vlog.BlockNumber], MessageHash: utils.ComputeMessageHash(event.Sender, event.Target, event.Value, event.MessageNonce, event.Message).String(), }) @@ -149,8 +202,8 @@ func (e *L1EventParser) ParseL1CrossChainEventLogs(ctx context.Context, logs []t MessageHash: event.MessageHash.String(), L1BlockNumber: vlog.BlockNumber, L1TxHash: vlog.TxHash.String(), - TxStatus: int(orm.TxStatusTypeRelayed), - MessageType: int(orm.MessageTypeL2SentMessage), + TxStatus: int(btypes.TxStatusTypeRelayed), + MessageType: int(btypes.MessageTypeL2SentMessage), }) case backendabi.L1FailedRelayedMessageEventSig: event := backendabi.L1FailedRelayedMessageEvent{} @@ -162,8 +215,8 @@ func (e *L1EventParser) ParseL1CrossChainEventLogs(ctx context.Context, logs []t MessageHash: event.MessageHash.String(), L1BlockNumber: vlog.BlockNumber, L1TxHash: vlog.TxHash.String(), - TxStatus: int(orm.TxStatusTypeFailedRelayed), - MessageType: int(orm.MessageTypeL2SentMessage), + TxStatus: int(btypes.TxStatusTypeFailedRelayed), + MessageType: int(btypes.MessageTypeL2SentMessage), }) } } @@ -192,7 +245,7 @@ func (e *L1EventParser) ParseL1BatchEventLogs(ctx context.Context, logs []types. return nil, err } l1BatchEvents = append(l1BatchEvents, &orm.BatchEvent{ - BatchStatus: int(orm.BatchStatusTypeCommitted), + BatchStatus: int(btypes.BatchStatusTypeCommitted), BatchIndex: event.BatchIndex.Uint64(), BatchHash: event.BatchHash.String(), StartBlockNumber: startBlock, @@ -206,7 +259,7 @@ func (e *L1EventParser) ParseL1BatchEventLogs(ctx context.Context, logs []types. return nil, err } l1BatchEvents = append(l1BatchEvents, &orm.BatchEvent{ - BatchStatus: int(orm.BatchStatusTypeReverted), + BatchStatus: int(btypes.BatchStatusTypeReverted), BatchIndex: event.BatchIndex.Uint64(), BatchHash: event.BatchHash.String(), L1BlockNumber: vlog.BlockNumber, @@ -218,7 +271,7 @@ func (e *L1EventParser) ParseL1BatchEventLogs(ctx context.Context, logs []types. return nil, err } l1BatchEvents = append(l1BatchEvents, &orm.BatchEvent{ - BatchStatus: int(orm.BatchStatusTypeFinalized), + BatchStatus: int(btypes.BatchStatusTypeFinalized), BatchIndex: event.BatchIndex.Uint64(), BatchHash: event.BatchHash.String(), L1BlockNumber: vlog.BlockNumber, @@ -248,7 +301,7 @@ func (e *L1EventParser) ParseL1MessageQueueEventLogs(logs []types.Log, l1Deposit // If the message hash is not found in the map, it's not a replayMessage or enforced tx (omitted); add it to the events. if _, exists := messageHashes[messageHash]; !exists { l1MessageQueueEvents = append(l1MessageQueueEvents, &orm.MessageQueueEvent{ - EventType: orm.MessageQueueEventTypeQueueTransaction, + EventType: btypes.MessageQueueEventTypeQueueTransaction, QueueIndex: event.QueueIndex, MessageHash: messageHash, TxHash: vlog.TxHash, @@ -263,7 +316,7 @@ func (e *L1EventParser) ParseL1MessageQueueEventLogs(logs []types.Log, l1Deposit skippedIndices := utils.GetSkippedQueueIndices(event.StartIndex.Uint64(), event.SkippedBitmap) for _, index := range skippedIndices { l1MessageQueueEvents = append(l1MessageQueueEvents, &orm.MessageQueueEvent{ - EventType: orm.MessageQueueEventTypeDequeueTransaction, + EventType: btypes.MessageQueueEventTypeDequeueTransaction, QueueIndex: index, }) } @@ -274,7 +327,7 @@ func (e *L1EventParser) ParseL1MessageQueueEventLogs(logs []types.Log, l1Deposit return nil, err } l1MessageQueueEvents = append(l1MessageQueueEvents, &orm.MessageQueueEvent{ - EventType: orm.MessageQueueEventTypeDropTransaction, + EventType: btypes.MessageQueueEventTypeDropTransaction, QueueIndex: event.Index.Uint64(), TxHash: vlog.TxHash, }) diff --git a/bridge-history-api/internal/logic/l1_fetcher.go b/bridge-history-api/internal/logic/l1_fetcher.go index f2436e6ad..8c3672694 100644 --- a/bridge-history-api/internal/logic/l1_fetcher.go +++ b/bridge-history-api/internal/logic/l1_fetcher.go @@ -16,6 +16,7 @@ import ( backendabi "scroll-tech/bridge-history-api/abi" "scroll-tech/bridge-history-api/internal/config" "scroll-tech/bridge-history-api/internal/orm" + btypes "scroll-tech/bridge-history-api/internal/types" "scroll-tech/bridge-history-api/internal/utils" ) @@ -25,11 +26,12 @@ const L1ReorgSafeDepth = 64 // L1FilterResult L1 fetcher result type L1FilterResult struct { - DepositMessages []*orm.CrossMessage - RelayedMessages []*orm.CrossMessage - BatchEvents []*orm.BatchEvent - MessageQueueEvents []*orm.MessageQueueEvent - RevertedTxs []*orm.CrossMessage + DepositMessages []*orm.CrossMessage + RelayedMessages []*orm.CrossMessage + BatchEvents []*orm.BatchEvent + MessageQueueEvents []*orm.MessageQueueEvent + RevertedTxs []*orm.CrossMessage + BridgeBatchDepositEvents []*orm.BridgeBatchDepositEvent } // L1FetcherLogic the L1 fetcher logic @@ -82,7 +84,7 @@ func NewL1FetcherLogic(cfg *config.FetcherConfig, db *gorm.DB, client *ethclient common.HexToAddress(cfg.GatewayRouterAddr), } - // Optional erc20 gateways. + // Optional gateways. if common.HexToAddress(cfg.USDCGatewayAddr) != (common.Address{}) { addressList = append(addressList, common.HexToAddress(cfg.USDCGatewayAddr)) gatewayList = append(gatewayList, common.HexToAddress(cfg.USDCGatewayAddr)) @@ -98,6 +100,11 @@ func NewL1FetcherLogic(cfg *config.FetcherConfig, db *gorm.DB, client *ethclient gatewayList = append(gatewayList, common.HexToAddress(cfg.PufferGatewayAddr)) } + if common.HexToAddress(cfg.BatchBridgeGatewayAddr) != (common.Address{}) { + addressList = append(addressList, common.HexToAddress(cfg.BatchBridgeGatewayAddr)) + gatewayList = append(gatewayList, common.HexToAddress(cfg.BatchBridgeGatewayAddr)) + } + log.Info("L1 Fetcher configured with the following address list", "addresses", addressList, "gateways", gatewayList) f := &L1FetcherLogic{ @@ -183,12 +190,12 @@ func (f *L1FetcherLogic) getRevertedTxs(ctx context.Context, from, to uint64, bl l1RevertedTxs = append(l1RevertedTxs, &orm.CrossMessage{ L1TxHash: tx.Hash().String(), - MessageType: int(orm.MessageTypeL1SentMessage), + MessageType: int(btypes.MessageTypeL1SentMessage), Sender: sender.String(), Receiver: (*tx.To()).String(), L1BlockNumber: receipt.BlockNumber.Uint64(), BlockTimestamp: block.Time(), - TxStatus: int(orm.TxStatusTypeSentTxReverted), + TxStatus: int(btypes.TxStatusTypeSentTxReverted), }) } } @@ -203,7 +210,7 @@ func (f *L1FetcherLogic) l1FetcherLogs(ctx context.Context, from, to uint64) ([] Topics: make([][]common.Hash, 1), } - query.Topics[0] = make([]common.Hash, 13) + query.Topics[0] = make([]common.Hash, 14) query.Topics[0][0] = backendabi.L1DepositETHSig query.Topics[0][1] = backendabi.L1DepositERC20Sig query.Topics[0][2] = backendabi.L1DepositERC721Sig @@ -217,6 +224,7 @@ func (f *L1FetcherLogic) l1FetcherLogs(ctx context.Context, from, to uint64) ([] query.Topics[0][10] = backendabi.L1QueueTransactionEventSig query.Topics[0][11] = backendabi.L1DequeueTransactionEventSig query.Topics[0][12] = backendabi.L1DropTransactionEventSig + query.Topics[0][13] = backendabi.L1BridgeBatchDepositSig eventLogs, err := f.client.FilterLogs(ctx, query) if err != nil { @@ -252,7 +260,7 @@ func (f *L1FetcherLogic) L1Fetcher(ctx context.Context, from, to uint64, lastBlo return false, 0, common.Hash{}, nil, err } - l1DepositMessages, l1RelayedMessages, err := f.parser.ParseL1CrossChainEventLogs(ctx, eventLogs, blockTimestampsMap) + l1DepositMessages, l1RelayedMessages, l1BridgeBatchDepositMessages, err := f.parser.ParseL1CrossChainEventLogs(ctx, eventLogs, blockTimestampsMap) if err != nil { log.Error("failed to parse L1 cross chain event logs", "from", from, "to", to, "err", err) return false, 0, common.Hash{}, nil, err @@ -271,11 +279,12 @@ func (f *L1FetcherLogic) L1Fetcher(ctx context.Context, from, to uint64, lastBlo } res := L1FilterResult{ - DepositMessages: l1DepositMessages, - RelayedMessages: l1RelayedMessages, - BatchEvents: l1BatchEvents, - MessageQueueEvents: l1MessageQueueEvents, - RevertedTxs: l1RevertedTxs, + DepositMessages: l1DepositMessages, + RelayedMessages: l1RelayedMessages, + BatchEvents: l1BatchEvents, + MessageQueueEvents: l1MessageQueueEvents, + RevertedTxs: l1RevertedTxs, + BridgeBatchDepositEvents: l1BridgeBatchDepositMessages, } f.updateMetrics(res) @@ -287,23 +296,23 @@ func (f *L1FetcherLogic) updateMetrics(res L1FilterResult) { f.l1FetcherLogicFetchedTotal.WithLabelValues("L1_failed_gateway_router_transaction").Add(float64(len(res.RevertedTxs))) for _, depositMessage := range res.DepositMessages { - switch orm.TokenType(depositMessage.TokenType) { - case orm.TokenTypeETH: + switch btypes.TokenType(depositMessage.TokenType) { + case btypes.TokenTypeETH: f.l1FetcherLogicFetchedTotal.WithLabelValues("L1_deposit_eth").Add(1) - case orm.TokenTypeERC20: + case btypes.TokenTypeERC20: f.l1FetcherLogicFetchedTotal.WithLabelValues("L1_deposit_erc20").Add(1) - case orm.TokenTypeERC721: + case btypes.TokenTypeERC721: f.l1FetcherLogicFetchedTotal.WithLabelValues("L1_deposit_erc721").Add(1) - case orm.TokenTypeERC1155: + case btypes.TokenTypeERC1155: f.l1FetcherLogicFetchedTotal.WithLabelValues("L1_deposit_erc1155").Add(1) } } for _, relayedMessage := range res.RelayedMessages { - switch orm.TxStatusType(relayedMessage.TxStatus) { - case orm.TxStatusTypeRelayed: + switch btypes.TxStatusType(relayedMessage.TxStatus) { + case btypes.TxStatusTypeRelayed: f.l1FetcherLogicFetchedTotal.WithLabelValues("L1_relayed_message").Add(1) - case orm.TxStatusTypeFailedRelayed: + case btypes.TxStatusTypeFailedRelayed: f.l1FetcherLogicFetchedTotal.WithLabelValues("L1_failed_relayed_message").Add(1) } // Have not tracked L1 relayed message reverted transaction yet. @@ -312,24 +321,33 @@ func (f *L1FetcherLogic) updateMetrics(res L1FilterResult) { } for _, batchEvent := range res.BatchEvents { - switch orm.BatchStatusType(batchEvent.BatchStatus) { - case orm.BatchStatusTypeCommitted: + switch btypes.BatchStatusType(batchEvent.BatchStatus) { + case btypes.BatchStatusTypeCommitted: f.l1FetcherLogicFetchedTotal.WithLabelValues("L1_commit_batch_event").Add(1) - case orm.BatchStatusTypeReverted: + case btypes.BatchStatusTypeReverted: f.l1FetcherLogicFetchedTotal.WithLabelValues("L1_revert_batch_event").Add(1) - case orm.BatchStatusTypeFinalized: + case btypes.BatchStatusTypeFinalized: f.l1FetcherLogicFetchedTotal.WithLabelValues("L1_finalize_batch_event").Add(1) } } for _, messageQueueEvent := range res.MessageQueueEvents { switch messageQueueEvent.EventType { - case orm.MessageQueueEventTypeQueueTransaction: // sendMessage is filtered out, only leaving replayMessage or appendEnforcedTransaction. + case btypes.MessageQueueEventTypeQueueTransaction: // sendMessage is filtered out, only leaving replayMessage or appendEnforcedTransaction. f.l1FetcherLogicFetchedTotal.WithLabelValues("L1_replay_message_or_enforced_transaction").Add(1) - case orm.MessageQueueEventTypeDequeueTransaction: + case btypes.MessageQueueEventTypeDequeueTransaction: f.l1FetcherLogicFetchedTotal.WithLabelValues("L1_skip_message").Add(1) - case orm.MessageQueueEventTypeDropTransaction: + case btypes.MessageQueueEventTypeDropTransaction: f.l1FetcherLogicFetchedTotal.WithLabelValues("L1_drop_message").Add(1) } } + + for _, bridgeBatchDepositEvent := range res.BridgeBatchDepositEvents { + switch btypes.TokenType(bridgeBatchDepositEvent.TokenType) { + case btypes.TokenTypeETH: + f.l1FetcherLogicFetchedTotal.WithLabelValues("L1_bridge_batch_deposit_eth").Add(1) + case btypes.TokenTypeERC20: + f.l1FetcherLogicFetchedTotal.WithLabelValues("L1_bridge_batch_deposit_erc20").Add(1) + } + } } diff --git a/bridge-history-api/internal/logic/l2_event_parser.go b/bridge-history-api/internal/logic/l2_event_parser.go index ae841d9b6..99fa85a24 100644 --- a/bridge-history-api/internal/logic/l2_event_parser.go +++ b/bridge-history-api/internal/logic/l2_event_parser.go @@ -3,6 +3,7 @@ package logic import ( "context" + "github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/common/hexutil" "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/ethclient" @@ -11,6 +12,7 @@ import ( backendabi "scroll-tech/bridge-history-api/abi" "scroll-tech/bridge-history-api/internal/config" "scroll-tech/bridge-history-api/internal/orm" + btypes "scroll-tech/bridge-history-api/internal/types" "scroll-tech/bridge-history-api/internal/utils" ) @@ -28,8 +30,72 @@ func NewL2EventParser(cfg *config.FetcherConfig, client *ethclient.Client) *L2Ev } } -// ParseL2EventLogs parses L2 watched events -func (e *L2EventParser) ParseL2EventLogs(ctx context.Context, logs []types.Log, blockTimestampsMap map[uint64]uint64) ([]*orm.CrossMessage, []*orm.CrossMessage, error) { +// ParseL2EventLogs parses L2 watchedevents +func (e *L2EventParser) ParseL2EventLogs(ctx context.Context, logs []types.Log, blockTimestampsMap map[uint64]uint64) ([]*orm.CrossMessage, []*orm.CrossMessage, []*orm.BridgeBatchDepositEvent, error) { + l2WithdrawMessages, l2RelayedMessages, err := e.ParseL2SingleCrossChainEventLogs(ctx, logs, blockTimestampsMap) + if err != nil { + return nil, nil, nil, err + } + + l2BridgeBatchDepositMessages, err := e.ParseL2BridgeBatchDepositCrossChainEventLogs(logs, blockTimestampsMap) + if err != nil { + return nil, nil, nil, err + } + return l2WithdrawMessages, l2RelayedMessages, l2BridgeBatchDepositMessages, nil +} + +// ParseL2BridgeBatchDepositCrossChainEventLogs parses L2 watched bridge batch deposit events +func (e *L2EventParser) ParseL2BridgeBatchDepositCrossChainEventLogs(logs []types.Log, blockTimestampsMap map[uint64]uint64) ([]*orm.BridgeBatchDepositEvent, error) { + var l2BridgeBatchDepositEvents []*orm.BridgeBatchDepositEvent + for _, vlog := range logs { + switch vlog.Topics[0] { + case backendabi.L2BridgeBatchDistributeSig: + event := backendabi.L2BatchBridgeGatewayBatchDistribute{} + err := utils.UnpackLog(backendabi.L2BatchBridgeGatewayABI, &event, "BatchDistribute", vlog) + if err != nil { + log.Error("Failed to unpack BatchDistribute event", "err", err) + return nil, err + } + + var tokenType btypes.TokenType + if event.L1Token == common.HexToAddress("0") { + tokenType = btypes.TokenTypeETH + } else { + tokenType = btypes.TokenTypeERC20 + } + + l2BridgeBatchDepositEvents = append(l2BridgeBatchDepositEvents, &orm.BridgeBatchDepositEvent{ + TokenType: int(tokenType), + BatchIndex: event.BatchIndex.Uint64(), + L2TokenAddress: event.L2Token.String(), + L2BlockNumber: vlog.BlockNumber, + L2TxHash: vlog.TxHash.String(), + TxStatus: int(btypes.TxStatusBridgeBatchDistribute), + BlockTimestamp: blockTimestampsMap[vlog.BlockNumber], + }) + case backendabi.L2BridgeBatchDistributeFailedSig: + event := backendabi.L2BatchBridgeGatewayDistributeFailed{} + err := utils.UnpackLog(backendabi.L2BatchBridgeGatewayABI, &event, "DistributeFailed", vlog) + if err != nil { + log.Error("Failed to unpack DistributeFailed event", "err", err) + return nil, err + } + l2BridgeBatchDepositEvents = append(l2BridgeBatchDepositEvents, &orm.BridgeBatchDepositEvent{ + BatchIndex: event.BatchIndex.Uint64(), + L2TokenAddress: event.L2Token.String(), + L2BlockNumber: vlog.BlockNumber, + L2TxHash: vlog.TxHash.String(), + TxStatus: int(btypes.TxStatusBridgeBatchDistributeFailed), + BlockTimestamp: blockTimestampsMap[vlog.BlockNumber], + Sender: event.Receiver.String(), + }) + } + } + return l2BridgeBatchDepositEvents, nil +} + +// ParseL2SingleCrossChainEventLogs parses L2 watched events +func (e *L2EventParser) ParseL2SingleCrossChainEventLogs(ctx context.Context, logs []types.Log, blockTimestampsMap map[uint64]uint64) ([]*orm.CrossMessage, []*orm.CrossMessage, error) { var l2WithdrawMessages []*orm.CrossMessage var l2RelayedMessages []*orm.CrossMessage for _, vlog := range logs { @@ -44,7 +110,7 @@ func (e *L2EventParser) ParseL2EventLogs(ctx context.Context, logs []types.Log, lastMessage := l2WithdrawMessages[len(l2WithdrawMessages)-1] lastMessage.Sender = event.From.String() lastMessage.Receiver = event.To.String() - lastMessage.TokenType = int(orm.TokenTypeETH) + lastMessage.TokenType = int(btypes.TokenTypeETH) lastMessage.TokenAmounts = event.Amount.String() case backendabi.L2WithdrawERC20Sig: event := backendabi.ERC20MessageEvent{} @@ -56,7 +122,7 @@ func (e *L2EventParser) ParseL2EventLogs(ctx context.Context, logs []types.Log, lastMessage := l2WithdrawMessages[len(l2WithdrawMessages)-1] lastMessage.Sender = event.From.String() lastMessage.Receiver = event.To.String() - lastMessage.TokenType = int(orm.TokenTypeERC20) + lastMessage.TokenType = int(btypes.TokenTypeERC20) lastMessage.L1TokenAddress = event.L1Token.String() lastMessage.L2TokenAddress = event.L2Token.String() lastMessage.TokenAmounts = event.Amount.String() @@ -70,7 +136,7 @@ func (e *L2EventParser) ParseL2EventLogs(ctx context.Context, logs []types.Log, lastMessage := l2WithdrawMessages[len(l2WithdrawMessages)-1] lastMessage.Sender = event.From.String() lastMessage.Receiver = event.To.String() - lastMessage.TokenType = int(orm.TokenTypeERC721) + lastMessage.TokenType = int(btypes.TokenTypeERC721) lastMessage.L1TokenAddress = event.L1Token.String() lastMessage.L2TokenAddress = event.L2Token.String() lastMessage.TokenIDs = event.TokenID.String() @@ -84,7 +150,7 @@ func (e *L2EventParser) ParseL2EventLogs(ctx context.Context, logs []types.Log, lastMessage := l2WithdrawMessages[len(l2WithdrawMessages)-1] lastMessage.Sender = event.From.String() lastMessage.Receiver = event.To.String() - lastMessage.TokenType = int(orm.TokenTypeERC721) + lastMessage.TokenType = int(btypes.TokenTypeERC721) lastMessage.L1TokenAddress = event.L1Token.String() lastMessage.L2TokenAddress = event.L2Token.String() lastMessage.TokenIDs = utils.ConvertBigIntArrayToString(event.TokenIDs) @@ -98,7 +164,7 @@ func (e *L2EventParser) ParseL2EventLogs(ctx context.Context, logs []types.Log, lastMessage := l2WithdrawMessages[len(l2WithdrawMessages)-1] lastMessage.Sender = event.From.String() lastMessage.Receiver = event.To.String() - lastMessage.TokenType = int(orm.TokenTypeERC1155) + lastMessage.TokenType = int(btypes.TokenTypeERC1155) lastMessage.L1TokenAddress = event.L1Token.String() lastMessage.L2TokenAddress = event.L2Token.String() lastMessage.TokenIDs = event.TokenID.String() @@ -113,7 +179,7 @@ func (e *L2EventParser) ParseL2EventLogs(ctx context.Context, logs []types.Log, lastMessage := l2WithdrawMessages[len(l2WithdrawMessages)-1] lastMessage.Sender = event.From.String() lastMessage.Receiver = event.To.String() - lastMessage.TokenType = int(orm.TokenTypeERC1155) + lastMessage.TokenType = int(btypes.TokenTypeERC1155) lastMessage.L1TokenAddress = event.L1Token.String() lastMessage.L2TokenAddress = event.L2Token.String() lastMessage.TokenIDs = utils.ConvertBigIntArrayToString(event.TokenIDs) @@ -134,7 +200,7 @@ func (e *L2EventParser) ParseL2EventLogs(ctx context.Context, logs []types.Log, MessageHash: utils.ComputeMessageHash(event.Sender, event.Target, event.Value, event.MessageNonce, event.Message).String(), Sender: from, Receiver: event.Target.String(), - TokenType: int(orm.TokenTypeETH), + TokenType: int(btypes.TokenTypeETH), L2TxHash: vlog.TxHash.String(), TokenAmounts: event.Value.String(), MessageFrom: event.Sender.String(), @@ -142,8 +208,8 @@ func (e *L2EventParser) ParseL2EventLogs(ctx context.Context, logs []types.Log, MessageValue: event.Value.String(), MessageNonce: event.MessageNonce.Uint64(), MessageData: hexutil.Encode(event.Message), - MessageType: int(orm.MessageTypeL2SentMessage), - TxStatus: int(orm.TxStatusTypeSent), + MessageType: int(btypes.MessageTypeL2SentMessage), + TxStatus: int(btypes.TxStatusTypeSent), BlockTimestamp: blockTimestampsMap[vlog.BlockNumber], L2BlockNumber: vlog.BlockNumber, }) @@ -158,8 +224,8 @@ func (e *L2EventParser) ParseL2EventLogs(ctx context.Context, logs []types.Log, MessageHash: event.MessageHash.String(), L2BlockNumber: vlog.BlockNumber, L2TxHash: vlog.TxHash.String(), - TxStatus: int(orm.TxStatusTypeRelayed), - MessageType: int(orm.MessageTypeL1SentMessage), + TxStatus: int(btypes.TxStatusTypeRelayed), + MessageType: int(btypes.MessageTypeL1SentMessage), }) case backendabi.L2FailedRelayedMessageEventSig: event := backendabi.L2RelayedMessageEvent{} @@ -172,8 +238,8 @@ func (e *L2EventParser) ParseL2EventLogs(ctx context.Context, logs []types.Log, MessageHash: event.MessageHash.String(), L2BlockNumber: vlog.BlockNumber, L2TxHash: vlog.TxHash.String(), - TxStatus: int(orm.TxStatusTypeFailedRelayed), - MessageType: int(orm.MessageTypeL1SentMessage), + TxStatus: int(btypes.TxStatusTypeFailedRelayed), + MessageType: int(btypes.MessageTypeL1SentMessage), }) } } diff --git a/bridge-history-api/internal/logic/l2_fetcher.go b/bridge-history-api/internal/logic/l2_fetcher.go index 24b583cb5..00f67499e 100644 --- a/bridge-history-api/internal/logic/l2_fetcher.go +++ b/bridge-history-api/internal/logic/l2_fetcher.go @@ -17,6 +17,7 @@ import ( backendabi "scroll-tech/bridge-history-api/abi" "scroll-tech/bridge-history-api/internal/config" "scroll-tech/bridge-history-api/internal/orm" + btypes "scroll-tech/bridge-history-api/internal/types" "scroll-tech/bridge-history-api/internal/utils" ) @@ -26,9 +27,10 @@ const L2ReorgSafeDepth = 256 // L2FilterResult the L2 filter result type L2FilterResult struct { - WithdrawMessages []*orm.CrossMessage - RelayedMessages []*orm.CrossMessage // relayed, failed relayed, relay tx reverted. - OtherRevertedTxs []*orm.CrossMessage // reverted txs except relay tx reverted. + WithdrawMessages []*orm.CrossMessage + RelayedMessages []*orm.CrossMessage // relayed, failed relayed, relay tx reverted. + OtherRevertedTxs []*orm.CrossMessage // reverted txs except relay tx reverted. + BridgeBatchDepositMessage []*orm.BridgeBatchDepositEvent } // L2FetcherLogic the L2 fetcher logic @@ -77,7 +79,7 @@ func NewL2FetcherLogic(cfg *config.FetcherConfig, db *gorm.DB, client *ethclient common.HexToAddress(cfg.GatewayRouterAddr), } - // Optional erc20 gateways. + // Optional gateways. if common.HexToAddress(cfg.USDCGatewayAddr) != (common.Address{}) { addressList = append(addressList, common.HexToAddress(cfg.USDCGatewayAddr)) gatewayList = append(gatewayList, common.HexToAddress(cfg.USDCGatewayAddr)) @@ -93,6 +95,11 @@ func NewL2FetcherLogic(cfg *config.FetcherConfig, db *gorm.DB, client *ethclient gatewayList = append(gatewayList, common.HexToAddress(cfg.PufferGatewayAddr)) } + if common.HexToAddress(cfg.BatchBridgeGatewayAddr) != (common.Address{}) { + addressList = append(addressList, common.HexToAddress(cfg.BatchBridgeGatewayAddr)) + gatewayList = append(gatewayList, common.HexToAddress(cfg.BatchBridgeGatewayAddr)) + } + log.Info("L2 Fetcher configured with the following address list", "addresses", addressList, "gateways", gatewayList) f := &L2FetcherLogic{ @@ -164,9 +171,9 @@ func (f *L2FetcherLogic) getRevertedTxs(ctx context.Context, from, to uint64, bl l2RevertedRelayedMessageTxs = append(l2RevertedRelayedMessageTxs, &orm.CrossMessage{ MessageHash: common.BytesToHash(crypto.Keccak256(tx.AsL1MessageTx().Data)).String(), L2TxHash: tx.Hash().String(), - TxStatus: int(orm.TxStatusTypeRelayTxReverted), + TxStatus: int(btypes.TxStatusTypeRelayTxReverted), L2BlockNumber: receipt.BlockNumber.Uint64(), - MessageType: int(orm.MessageTypeL1SentMessage), + MessageType: int(btypes.MessageTypeL1SentMessage), }) } continue @@ -194,12 +201,12 @@ func (f *L2FetcherLogic) getRevertedTxs(ctx context.Context, from, to uint64, bl l2RevertedUserTxs = append(l2RevertedUserTxs, &orm.CrossMessage{ L2TxHash: tx.Hash().String(), - MessageType: int(orm.MessageTypeL2SentMessage), + MessageType: int(btypes.MessageTypeL2SentMessage), Sender: sender.String(), Receiver: (*tx.To()).String(), L2BlockNumber: receipt.BlockNumber.Uint64(), BlockTimestamp: block.Time(), - TxStatus: int(orm.TxStatusTypeSentTxReverted), + TxStatus: int(btypes.TxStatusTypeSentTxReverted), }) } } @@ -214,7 +221,7 @@ func (f *L2FetcherLogic) l2FetcherLogs(ctx context.Context, from, to uint64) ([] Addresses: f.addressList, Topics: make([][]common.Hash, 1), } - query.Topics[0] = make([]common.Hash, 7) + query.Topics[0] = make([]common.Hash, 9) query.Topics[0][0] = backendabi.L2WithdrawETHSig query.Topics[0][1] = backendabi.L2WithdrawERC20Sig query.Topics[0][2] = backendabi.L2WithdrawERC721Sig @@ -222,6 +229,8 @@ func (f *L2FetcherLogic) l2FetcherLogs(ctx context.Context, from, to uint64) ([] query.Topics[0][4] = backendabi.L2SentMessageEventSig query.Topics[0][5] = backendabi.L2RelayedMessageEventSig query.Topics[0][6] = backendabi.L2FailedRelayedMessageEventSig + query.Topics[0][7] = backendabi.L2BridgeBatchDistributeSig + query.Topics[0][8] = backendabi.L2BridgeBatchDistributeFailedSig eventLogs, err := f.client.FilterLogs(ctx, query) if err != nil { @@ -257,16 +266,17 @@ func (f *L2FetcherLogic) L2Fetcher(ctx context.Context, from, to uint64, lastBlo return false, 0, common.Hash{}, nil, err } - l2WithdrawMessages, l2RelayedMessages, err := f.parser.ParseL2EventLogs(ctx, eventLogs, blockTimestampsMap) + l2WithdrawMessages, l2RelayedMessages, l2BridgeBatchDepositMessages, err := f.parser.ParseL2EventLogs(ctx, eventLogs, blockTimestampsMap) if err != nil { log.Error("failed to parse L2 event logs", "from", from, "to", to, "err", err) return false, 0, common.Hash{}, nil, err } res := L2FilterResult{ - WithdrawMessages: l2WithdrawMessages, - RelayedMessages: append(l2RelayedMessages, revertedRelayMsgs...), - OtherRevertedTxs: revertedUserTxs, + WithdrawMessages: l2WithdrawMessages, + RelayedMessages: append(l2RelayedMessages, revertedRelayMsgs...), + OtherRevertedTxs: revertedUserTxs, + BridgeBatchDepositMessage: l2BridgeBatchDepositMessages, } f.updateMetrics(res) @@ -278,28 +288,37 @@ func (f *L2FetcherLogic) updateMetrics(res L2FilterResult) { f.l2FetcherLogicFetchedTotal.WithLabelValues("L2_failed_gateway_router_transaction").Add(float64(len(res.OtherRevertedTxs))) for _, withdrawMessage := range res.WithdrawMessages { - switch orm.TokenType(withdrawMessage.TokenType) { - case orm.TokenTypeETH: + switch btypes.TokenType(withdrawMessage.TokenType) { + case btypes.TokenTypeETH: f.l2FetcherLogicFetchedTotal.WithLabelValues("L2_withdraw_eth").Add(1) - case orm.TokenTypeERC20: + case btypes.TokenTypeERC20: f.l2FetcherLogicFetchedTotal.WithLabelValues("L2_withdraw_erc20").Add(1) - case orm.TokenTypeERC721: + case btypes.TokenTypeERC721: f.l2FetcherLogicFetchedTotal.WithLabelValues("L2_withdraw_erc721").Add(1) - case orm.TokenTypeERC1155: + case btypes.TokenTypeERC1155: f.l2FetcherLogicFetchedTotal.WithLabelValues("L2_withdraw_erc1155").Add(1) } } for _, relayedMessage := range res.RelayedMessages { - switch orm.TxStatusType(relayedMessage.TxStatus) { - case orm.TxStatusTypeRelayed: + switch btypes.TxStatusType(relayedMessage.TxStatus) { + case btypes.TxStatusTypeRelayed: f.l2FetcherLogicFetchedTotal.WithLabelValues("L2_relayed_message").Add(1) - case orm.TxStatusTypeFailedRelayed: + case btypes.TxStatusTypeFailedRelayed: f.l2FetcherLogicFetchedTotal.WithLabelValues("L2_failed_relayed_message").Add(1) - case orm.TxStatusTypeRelayTxReverted: + case btypes.TxStatusTypeRelayTxReverted: f.l2FetcherLogicFetchedTotal.WithLabelValues("L2_reverted_relayed_message_transaction").Add(1) } } + + for _, bridgeBatchDepositMessage := range res.BridgeBatchDepositMessage { + switch btypes.TxStatusType(bridgeBatchDepositMessage.TxStatus) { + case btypes.TxStatusBridgeBatchDistribute: + f.l2FetcherLogicFetchedTotal.WithLabelValues("L2_bridge_batch_distribute_message").Add(1) + case btypes.TxStatusBridgeBatchDistributeFailed: + f.l2FetcherLogicFetchedTotal.WithLabelValues("L2_bridge_batch_distribute_failed_message").Add(1) + } + } } func isTransactionToGateway(tx *types.Transaction, gatewayList []common.Address) bool { diff --git a/bridge-history-api/internal/orm/batch_event.go b/bridge-history-api/internal/orm/batch_event.go index 67135561f..926a1e3b0 100644 --- a/bridge-history-api/internal/orm/batch_event.go +++ b/bridge-history-api/internal/orm/batch_event.go @@ -7,26 +7,8 @@ import ( "gorm.io/gorm" "gorm.io/gorm/clause" -) -// BatchStatusType represents the type of batch status. -type BatchStatusType int - -// Constants for BatchStatusType. -const ( - BatchStatusTypeUnknown BatchStatusType = iota - BatchStatusTypeCommitted - BatchStatusTypeReverted - BatchStatusTypeFinalized -) - -// UpdateStatusType represents the whether batch info is updated in message table. -type UpdateStatusType int - -// Constants for UpdateStatusType. -const ( - UpdateStatusTypeUnupdated UpdateStatusType = iota - UpdateStatusTypeUpdated + btypes "scroll-tech/bridge-history-api/internal/types" ) // BatchEvent represents a batch event. @@ -77,8 +59,8 @@ func (c *BatchEvent) GetFinalizedBatchesLEBlockHeight(ctx context.Context, block db := c.db.WithContext(ctx) db = db.Model(&BatchEvent{}) db = db.Where("end_block_number <= ?", blockHeight) - db = db.Where("batch_status = ?", BatchStatusTypeFinalized) - db = db.Where("update_status = ?", UpdateStatusTypeUnupdated) + db = db.Where("batch_status = ?", btypes.BatchStatusTypeFinalized) + db = db.Where("update_status = ?", btypes.UpdateStatusTypeUnupdated) db = db.Order("batch_index asc") if err := db.Find(&batches).Error; err != nil { if err == gorm.ErrRecordNotFound { @@ -96,8 +78,8 @@ func (c *BatchEvent) InsertOrUpdateBatchEvents(ctx context.Context, l1BatchEvent db = db.WithContext(ctx) db = db.Model(&BatchEvent{}) updateFields := make(map[string]interface{}) - switch BatchStatusType(l1BatchEvent.BatchStatus) { - case BatchStatusTypeCommitted: + switch btypes.BatchStatusType(l1BatchEvent.BatchStatus) { + case btypes.BatchStatusTypeCommitted: // Use the clause to either insert or ignore on conflict db = db.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "batch_hash"}}, @@ -106,17 +88,17 @@ func (c *BatchEvent) InsertOrUpdateBatchEvents(ctx context.Context, l1BatchEvent if err := db.Create(l1BatchEvent).Error; err != nil { return fmt.Errorf("failed to insert or ignore batch event, error: %w", err) } - case BatchStatusTypeFinalized: + case btypes.BatchStatusTypeFinalized: db = db.Where("batch_index = ?", l1BatchEvent.BatchIndex) db = db.Where("batch_hash = ?", l1BatchEvent.BatchHash) - updateFields["batch_status"] = BatchStatusTypeFinalized + updateFields["batch_status"] = btypes.BatchStatusTypeFinalized if err := db.Updates(updateFields).Error; err != nil { return fmt.Errorf("failed to update batch event, error: %w", err) } - case BatchStatusTypeReverted: + case btypes.BatchStatusTypeReverted: db = db.Where("batch_index = ?", l1BatchEvent.BatchIndex) db = db.Where("batch_hash = ?", l1BatchEvent.BatchHash) - updateFields["batch_status"] = BatchStatusTypeReverted + updateFields["batch_status"] = btypes.BatchStatusTypeReverted if err := db.Updates(updateFields).Error; err != nil { return fmt.Errorf("failed to update batch event, error: %w", err) } @@ -135,7 +117,7 @@ func (c *BatchEvent) UpdateBatchEventStatus(ctx context.Context, batchIndex uint db = db.Model(&BatchEvent{}) db = db.Where("batch_index = ?", batchIndex) updateFields := map[string]interface{}{ - "update_status": UpdateStatusTypeUpdated, + "update_status": btypes.UpdateStatusTypeUpdated, } if err := db.Updates(updateFields).Error; err != nil { return fmt.Errorf("failed to update batch event status, batchIndex: %d, error: %w", batchIndex, err) diff --git a/bridge-history-api/internal/orm/bridge_batch_deposit.go b/bridge-history-api/internal/orm/bridge_batch_deposit.go new file mode 100644 index 000000000..cd0876c18 --- /dev/null +++ b/bridge-history-api/internal/orm/bridge_batch_deposit.go @@ -0,0 +1,163 @@ +package orm + +import ( + "context" + "errors" + "fmt" + "time" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + + "scroll-tech/bridge-history-api/internal/types" +) + +// BridgeBatchDepositEvent represents the bridge batch deposit event. +type BridgeBatchDepositEvent struct { + db *gorm.DB `gorm:"column:-"` + + ID uint64 `json:"id" gorm:"column:id;primary_key"` + TokenType int `json:"token_type" gorm:"column:token_type"` + Sender string `json:"sender" gorm:"column:sender"` + BatchIndex uint64 `json:"batch_index" gorm:"column:batch_index"` + TokenAmount string `json:"token_amount" gorm:"column:token_amount"` + Fee string `json:"fee" gorm:"column:fee"` + L1TokenAddress string `json:"l1_token_address" gorm:"column:l1_token_address"` + L2TokenAddress string `json:"l2_token_address" gorm:"column:l2_token_address"` + L1BlockNumber uint64 `json:"l1_block_number" gorm:"column:l1_block_number"` + L2BlockNumber uint64 `json:"l2_block_number" gorm:"column:l2_block_number"` + L1TxHash string `json:"l1_tx_hash" gorm:"column:l1_tx_hash"` + L1LogIndex uint `json:"l1_log_index" gorm:"column:l1_log_index"` + L2TxHash string `json:"l2_tx_hash" gorm:"column:l2_tx_hash"` + TxStatus int `json:"tx_status" gorm:"column:tx_status"` + BlockTimestamp uint64 `json:"block_timestamp" gorm:"column:block_timestamp"` + CreatedAt time.Time `json:"created_at" gorm:"column:created_at"` + UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"` + DeletedAt *time.Time `json:"deleted_at" gorm:"column:deleted_at"` +} + +// TableName returns the table name for the BridgeBatchDepositEvent model. +func (*BridgeBatchDepositEvent) TableName() string { + return "bridge_batch_deposit_event_v2" +} + +// NewBridgeBatchDepositEvent returns a new instance of BridgeBatchDepositEvent. +func NewBridgeBatchDepositEvent(db *gorm.DB) *BridgeBatchDepositEvent { + return &BridgeBatchDepositEvent{db: db} +} + +// GetTxsByAddress returns the txs by address +func (c *BridgeBatchDepositEvent) GetTxsByAddress(ctx context.Context, sender string) ([]*BridgeBatchDepositEvent, error) { + var messages []*BridgeBatchDepositEvent + db := c.db.WithContext(ctx) + db = db.Model(&BridgeBatchDepositEvent{}) + db = db.Where("sender = ?", sender) + db = db.Order("block_timestamp desc") + db = db.Limit(500) + if err := db.Find(&messages).Error; err != nil { + return nil, fmt.Errorf("failed to get all txs by sender address, sender: %v, error: %w", sender, err) + } + return messages, nil +} + +// GetMessagesByTxHashes retrieves all BridgeBatchDepositEvent from the database that match the provided transaction hashes. +func (c *BridgeBatchDepositEvent) GetMessagesByTxHashes(ctx context.Context, txHashes []string) ([]*BridgeBatchDepositEvent, error) { + var messages []*BridgeBatchDepositEvent + db := c.db.WithContext(ctx) + db = db.Model(&BridgeBatchDepositEvent{}) + db = db.Where("l1_tx_hash in (?) or l2_tx_hash in (?)", txHashes, txHashes) + if err := db.Find(&messages).Error; err != nil { + return nil, fmt.Errorf("failed to GetMessagesByTxHashes by tx hashes, tx hashes: %v, error: %w", txHashes, err) + } + return messages, nil +} + +// GetMessageL1SyncedHeightInDB returns the l1 latest bridge batch deposit message height from the database +func (c *BridgeBatchDepositEvent) GetMessageL1SyncedHeightInDB(ctx context.Context) (uint64, error) { + var message BridgeBatchDepositEvent + db := c.db.WithContext(ctx) + db = db.Model(&BridgeBatchDepositEvent{}) + db = db.Order("l1_block_number desc") + + err := db.First(&message).Error + if err != nil && errors.Is(gorm.ErrRecordNotFound, err) { + return 0, nil + } + + if err != nil { + return 0, fmt.Errorf("failed to get l1 latest processed height, error: %w", err) + } + + return message.L1BlockNumber, nil +} + +// GetMessageL2SyncedHeightInDB returns the l2 latest bridge batch deposit message height from the database +func (c *BridgeBatchDepositEvent) GetMessageL2SyncedHeightInDB(ctx context.Context) (uint64, error) { + var message BridgeBatchDepositEvent + db := c.db.WithContext(ctx) + db = db.Model(&BridgeBatchDepositEvent{}) + db = db.Order("l2_block_number desc") + + err := db.First(&message).Error + if err != nil && errors.Is(gorm.ErrRecordNotFound, err) { + return 0, nil + } + + if err != nil { + return 0, fmt.Errorf("failed to get l2 latest processed height, error: %w", err) + } + + return message.L2BlockNumber, nil +} + +// InsertOrUpdateL1BridgeBatchDepositEvent inserts or updates a new L1 BridgeBatchDepositEvent +func (c *BridgeBatchDepositEvent) InsertOrUpdateL1BridgeBatchDepositEvent(ctx context.Context, l1BatchDepositEvents []*BridgeBatchDepositEvent) error { + if len(l1BatchDepositEvents) == 0 { + return nil + } + + db := c.db + db = db.WithContext(ctx) + db = db.Model(&BridgeBatchDepositEvent{}) + db = db.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "l1_tx_hash"}, {Name: "l1_log_index"}}, + DoUpdates: clause.AssignmentColumns([]string{"token_amount", "fee", "l1_block_number", "l1_token_address", "tx_status", "block_timestamp"}), + }) + if err := db.Create(l1BatchDepositEvents).Error; err != nil { + return fmt.Errorf("failed to insert message, error: %w", err) + } + return nil +} + +// UpdateBatchEventStatus updates the tx_status of BridgeBatchDepositEvent given batch index +func (c *BridgeBatchDepositEvent) UpdateBatchEventStatus(ctx context.Context, distributeMessage *BridgeBatchDepositEvent) error { + db := c.db.WithContext(ctx) + db = db.Model(&BridgeBatchDepositEvent{}) + db = db.Where("batch_index = ?", distributeMessage.BatchIndex) + db = db.Where("token_type = ?", distributeMessage.TokenType) + updateFields := map[string]interface{}{ + "l2_token_address": distributeMessage.L2TokenAddress, + "l2_block_number": distributeMessage.L2BlockNumber, + "l2_tx_hash": distributeMessage.L2TxHash, + "tx_status": types.TxStatusBridgeBatchDistribute, + } + if err := db.Updates(updateFields).Error; err != nil { + return fmt.Errorf("failed to UpdateBatchEventStatus, batchIndex: %d, error: %w", distributeMessage.BatchIndex, err) + } + return nil +} + +// UpdateDistributeFailedStatus updates the tx_status of BridgeBatchDepositEvent given batch index and senders +func (c *BridgeBatchDepositEvent) UpdateDistributeFailedStatus(ctx context.Context, batchIndex uint64, senders []string) error { + db := c.db.WithContext(ctx) + db = db.Model(&BridgeBatchDepositEvent{}) + db = db.Where("batch_index = ?", batchIndex) + db = db.Where("sender in (?)", senders) + updateFields := map[string]interface{}{ + "tx_status": types.TxStatusBridgeBatchDistributeFailed, + } + if err := db.Updates(updateFields).Error; err != nil { + return fmt.Errorf("failed to UpdateDistributeFailedStatus, batchIndex: %d, senders:%v, error: %w", batchIndex, senders, err) + } + return nil +} diff --git a/bridge-history-api/internal/orm/cross_message.go b/bridge-history-api/internal/orm/cross_message.go index 0ffe1d93b..786571db5 100644 --- a/bridge-history-api/internal/orm/cross_message.go +++ b/bridge-history-api/internal/orm/cross_message.go @@ -8,75 +8,15 @@ import ( "github.com/scroll-tech/go-ethereum/common" "gorm.io/gorm" "gorm.io/gorm/clause" -) -// TokenType represents the type of token. -type TokenType int + "scroll-tech/bridge-history-api/internal/types" -// Constants for TokenType. -const ( - TokenTypeUnknown TokenType = iota - TokenTypeETH - TokenTypeERC20 - TokenTypeERC721 - TokenTypeERC1155 -) - -// MessageType represents the type of message. -type MessageType int - -// Constants for MessageType. -const ( - MessageTypeUnknown MessageType = iota - MessageTypeL1SentMessage - MessageTypeL2SentMessage -) - -// TxStatusType represents the status of a transaction. -type TxStatusType int - -// Constants for TxStatusType. -const ( - // TxStatusTypeSent is one of the initial statuses for cross-chain messages. - // It is used as the default value to prevent overwriting the transaction status in scenarios where the message status might change - // from a later status (e.g., relayed) back to "sent". - // Example flow (L1 -> L2 message, and L1 fetcher is slower than L2 fetcher): - // 1. The relayed message is first tracked and processed, setting tx_status to TxStatusTypeRelayed. - // 2. The sent message is later processed (same cross-chain message), the tx_status should not over-write TxStatusTypeRelayed. - TxStatusTypeSent TxStatusType = iota - TxStatusTypeSentTxReverted // Not track message hash, thus will not be processed again anymore. - TxStatusTypeRelayed // Terminal status. - // Retry: this often occurs due to an out of gas (OOG) issue if the transaction was initiated via the frontend. - TxStatusTypeFailedRelayed - // Retry: this often occurs due to an out of gas (OOG) issue if the transaction was initiated via the frontend. - TxStatusTypeRelayTxReverted - TxStatusTypeSkipped - TxStatusTypeDropped // Terminal status. -) - -// RollupStatusType represents the status of a rollup. -type RollupStatusType int - -// Constants for RollupStatusType. -const ( - RollupStatusTypeUnknown RollupStatusType = iota - RollupStatusTypeFinalized // only batch finalized status is used. -) - -// MessageQueueEventType represents the type of message queue event. -type MessageQueueEventType int - -// Constants for MessageQueueEventType. -const ( - MessageQueueEventTypeUnknown MessageQueueEventType = iota - MessageQueueEventTypeQueueTransaction - MessageQueueEventTypeDequeueTransaction - MessageQueueEventTypeDropTransaction + btypes "scroll-tech/bridge-history-api/internal/types" ) // MessageQueueEvent struct represents the details of a batch event. type MessageQueueEvent struct { - EventType MessageQueueEventType + EventType btypes.MessageQueueEventType QueueIndex uint64 // Track replay tx hash and refund tx hash. @@ -132,15 +72,15 @@ func NewCrossMessage(db *gorm.DB) *CrossMessage { } // GetMessageSyncedHeightInDB returns the latest synced cross message height from the database for a given message type. -func (c *CrossMessage) GetMessageSyncedHeightInDB(ctx context.Context, messageType MessageType) (uint64, error) { +func (c *CrossMessage) GetMessageSyncedHeightInDB(ctx context.Context, messageType btypes.MessageType) (uint64, error) { var message CrossMessage db := c.db.WithContext(ctx) db = db.Model(&CrossMessage{}) db = db.Where("message_type = ?", messageType) switch { - case messageType == MessageTypeL1SentMessage: + case messageType == btypes.MessageTypeL1SentMessage: db = db.Order("l1_block_number desc") - case messageType == MessageTypeL2SentMessage: + case messageType == btypes.MessageTypeL2SentMessage: db = db.Order("l2_block_number desc") } if err := db.First(&message).Error; err != nil { @@ -150,9 +90,9 @@ func (c *CrossMessage) GetMessageSyncedHeightInDB(ctx context.Context, messageTy return 0, fmt.Errorf("failed to get latest processed height, type: %v, error: %w", messageType, err) } switch { - case messageType == MessageTypeL1SentMessage: + case messageType == btypes.MessageTypeL1SentMessage: return message.L1BlockNumber, nil - case messageType == MessageTypeL2SentMessage: + case messageType == btypes.MessageTypeL2SentMessage: return message.L2BlockNumber, nil default: return 0, fmt.Errorf("invalid message type: %v", messageType) @@ -164,8 +104,8 @@ func (c *CrossMessage) GetL2LatestFinalizedWithdrawal(ctx context.Context) (*Cro var message CrossMessage db := c.db.WithContext(ctx) db = db.Model(&CrossMessage{}) - db = db.Where("message_type = ?", MessageTypeL2SentMessage) - db = db.Where("rollup_status = ?", RollupStatusTypeFinalized) + db = db.Where("message_type = ?", btypes.MessageTypeL2SentMessage) + db = db.Where("rollup_status = ?", btypes.RollupStatusTypeFinalized) db = db.Order("message_nonce desc") if err := db.First(&message).Error; err != nil { if err == gorm.ErrRecordNotFound { @@ -183,8 +123,8 @@ func (c *CrossMessage) GetL2WithdrawalsByBlockRange(ctx context.Context, startBl db = db.Model(&CrossMessage{}) db = db.Where("l2_block_number >= ?", startBlock) db = db.Where("l2_block_number <= ?", endBlock) - db = db.Where("tx_status != ?", TxStatusTypeSentTxReverted) - db = db.Where("message_type = ?", MessageTypeL2SentMessage) + db = db.Where("tx_status != ?", types.TxStatusTypeSentTxReverted) + db = db.Where("message_type = ?", btypes.MessageTypeL2SentMessage) db = db.Order("message_nonce asc") if err := db.Find(&messages).Error; err != nil { if err == gorm.ErrRecordNotFound { @@ -212,8 +152,8 @@ func (c *CrossMessage) GetL2UnclaimedWithdrawalsByAddress(ctx context.Context, s var messages []*CrossMessage db := c.db.WithContext(ctx) db = db.Model(&CrossMessage{}) - db = db.Where("message_type = ?", MessageTypeL2SentMessage) - db = db.Where("tx_status = ?", TxStatusTypeSent) + db = db.Where("message_type = ?", btypes.MessageTypeL2SentMessage) + db = db.Where("tx_status = ?", types.TxStatusTypeSent) db = db.Where("sender = ?", sender) db = db.Order("block_timestamp desc") db = db.Limit(500) @@ -228,7 +168,7 @@ func (c *CrossMessage) GetL2WithdrawalsByAddress(ctx context.Context, sender str var messages []*CrossMessage db := c.db.WithContext(ctx) db = db.Model(&CrossMessage{}) - db = db.Where("message_type = ?", MessageTypeL2SentMessage) + db = db.Where("message_type = ?", btypes.MessageTypeL2SentMessage) db = db.Where("sender = ?", sender) db = db.Order("block_timestamp desc") db = db.Limit(500) @@ -261,22 +201,22 @@ func (c *CrossMessage) UpdateL1MessageQueueEventsInfo(ctx context.Context, l1Mes db = db.Model(&CrossMessage{}) txStatusUpdateFields := make(map[string]interface{}) switch l1MessageQueueEvent.EventType { - case MessageQueueEventTypeQueueTransaction: + case btypes.MessageQueueEventTypeQueueTransaction: continue - case MessageQueueEventTypeDequeueTransaction: + case btypes.MessageQueueEventTypeDequeueTransaction: // do not over-write terminal statuses. - db = db.Where("tx_status != ?", TxStatusTypeRelayed) - db = db.Where("tx_status != ?", TxStatusTypeDropped) + db = db.Where("tx_status != ?", types.TxStatusTypeRelayed) + db = db.Where("tx_status != ?", types.TxStatusTypeDropped) db = db.Where("message_nonce = ?", l1MessageQueueEvent.QueueIndex) - db = db.Where("message_type = ?", MessageTypeL1SentMessage) - txStatusUpdateFields["tx_status"] = TxStatusTypeSkipped - case MessageQueueEventTypeDropTransaction: + db = db.Where("message_type = ?", btypes.MessageTypeL1SentMessage) + txStatusUpdateFields["tx_status"] = types.TxStatusTypeSkipped + case btypes.MessageQueueEventTypeDropTransaction: // do not over-write terminal statuses. - db = db.Where("tx_status != ?", TxStatusTypeRelayed) - db = db.Where("tx_status != ?", TxStatusTypeDropped) + db = db.Where("tx_status != ?", types.TxStatusTypeRelayed) + db = db.Where("tx_status != ?", types.TxStatusTypeDropped) db = db.Where("message_nonce = ?", l1MessageQueueEvent.QueueIndex) - db = db.Where("message_type = ?", MessageTypeL1SentMessage) - txStatusUpdateFields["tx_status"] = TxStatusTypeDropped + db = db.Where("message_type = ?", btypes.MessageTypeL1SentMessage) + txStatusUpdateFields["tx_status"] = types.TxStatusTypeDropped } if err := db.Updates(txStatusUpdateFields).Error; err != nil { return fmt.Errorf("failed to update tx statuses of L1 message queue events, update fields: %v, error: %w", txStatusUpdateFields, err) @@ -290,9 +230,9 @@ func (c *CrossMessage) UpdateL1MessageQueueEventsInfo(ctx context.Context, l1Mes db = db.Model(&CrossMessage{}) txHashUpdateFields := make(map[string]interface{}) switch l1MessageQueueEvent.EventType { - case MessageQueueEventTypeDequeueTransaction: + case btypes.MessageQueueEventTypeDequeueTransaction: continue - case MessageQueueEventTypeQueueTransaction: + case btypes.MessageQueueEventTypeQueueTransaction: // only replayMessages or enforced txs (whose message hashes would not be found), sendMessages have been filtered out. // replayMessage case: // First SentMessage in L1: https://sepolia.etherscan.io/tx/0xbee4b631312448fcc2caac86e4dccf0a2ae0a88acd6c5fd8764d39d746e472eb @@ -304,9 +244,9 @@ func (c *CrossMessage) UpdateL1MessageQueueEventsInfo(ctx context.Context, l1Mes // Ref: https://github.com/scroll-tech/scroll/blob/v4.3.44/contracts/src/L1/L1ScrollMessenger.sol#L187-L190 db = db.Where("message_hash = ?", l1MessageQueueEvent.MessageHash.String()) txHashUpdateFields["l1_replay_tx_hash"] = l1MessageQueueEvent.TxHash.String() - case MessageQueueEventTypeDropTransaction: + case btypes.MessageQueueEventTypeDropTransaction: db = db.Where("message_nonce = ?", l1MessageQueueEvent.QueueIndex) - db = db.Where("message_type = ?", MessageTypeL1SentMessage) + db = db.Where("message_type = ?", btypes.MessageTypeL1SentMessage) txHashUpdateFields["l1_refund_tx_hash"] = l1MessageQueueEvent.TxHash.String() } if err := db.Updates(txHashUpdateFields).Error; err != nil { @@ -320,12 +260,12 @@ func (c *CrossMessage) UpdateL1MessageQueueEventsInfo(ctx context.Context, l1Mes func (c *CrossMessage) UpdateBatchStatusOfL2Withdrawals(ctx context.Context, startBlockNumber, endBlockNumber, batchIndex uint64) error { db := c.db.WithContext(ctx) db = db.Model(&CrossMessage{}) - db = db.Where("message_type = ?", MessageTypeL2SentMessage) + db = db.Where("message_type = ?", btypes.MessageTypeL2SentMessage) db = db.Where("l2_block_number >= ?", startBlockNumber) db = db.Where("l2_block_number <= ?", endBlockNumber) updateFields := make(map[string]interface{}) updateFields["batch_index"] = batchIndex - updateFields["rollup_status"] = RollupStatusTypeFinalized + updateFields["rollup_status"] = btypes.RollupStatusTypeFinalized if err := db.Updates(updateFields).Error; err != nil { return fmt.Errorf("failed to update batch status of L2 sent messages, start: %v, end: %v, index: %v, error: %w", startBlockNumber, endBlockNumber, batchIndex, err) } @@ -462,7 +402,7 @@ func (c *CrossMessage) InsertOrUpdateL2RelayedMessagesOfL1Deposits(ctx context.C mergedL2RelayedMessages := make(map[string]*CrossMessage) for _, message := range l2RelayedMessages { if existing, found := mergedL2RelayedMessages[message.MessageHash]; found { - if TxStatusType(message.TxStatus) == TxStatusTypeRelayed || message.L2BlockNumber > existing.L2BlockNumber { + if types.TxStatusType(message.TxStatus) == types.TxStatusTypeRelayed || message.L2BlockNumber > existing.L2BlockNumber { mergedL2RelayedMessages[message.MessageHash] = message } } else { @@ -489,8 +429,8 @@ func (c *CrossMessage) InsertOrUpdateL2RelayedMessagesOfL1Deposits(ctx context.C Exprs: []clause.Expression{ clause.And( // do not over-write terminal statuses. - clause.Neq{Column: "cross_message_v2.tx_status", Value: TxStatusTypeRelayed}, - clause.Neq{Column: "cross_message_v2.tx_status", Value: TxStatusTypeDropped}, + clause.Neq{Column: "cross_message_v2.tx_status", Value: types.TxStatusTypeRelayed}, + clause.Neq{Column: "cross_message_v2.tx_status", Value: types.TxStatusTypeDropped}, ), }, }, @@ -520,7 +460,7 @@ func (c *CrossMessage) InsertOrUpdateL1RelayedMessagesOfL2Withdrawals(ctx contex mergedL1RelayedMessages := make(map[string]*CrossMessage) for _, message := range l1RelayedMessages { if existing, found := mergedL1RelayedMessages[message.MessageHash]; found { - if TxStatusType(message.TxStatus) == TxStatusTypeRelayed || message.L1BlockNumber > existing.L1BlockNumber { + if types.TxStatusType(message.TxStatus) == types.TxStatusTypeRelayed || message.L1BlockNumber > existing.L1BlockNumber { mergedL1RelayedMessages[message.MessageHash] = message } } else { @@ -541,8 +481,8 @@ func (c *CrossMessage) InsertOrUpdateL1RelayedMessagesOfL2Withdrawals(ctx contex Exprs: []clause.Expression{ clause.And( // do not over-write terminal statuses. - clause.Neq{Column: "cross_message_v2.tx_status", Value: TxStatusTypeRelayed}, - clause.Neq{Column: "cross_message_v2.tx_status", Value: TxStatusTypeDropped}, + clause.Neq{Column: "cross_message_v2.tx_status", Value: types.TxStatusTypeRelayed}, + clause.Neq{Column: "cross_message_v2.tx_status", Value: types.TxStatusTypeDropped}, ), }, }, diff --git a/bridge-history-api/internal/orm/migrate/migrations/00003_bridge_batch_deposit_event_v2.sql b/bridge-history-api/internal/orm/migrate/migrations/00003_bridge_batch_deposit_event_v2.sql new file mode 100644 index 000000000..38861b9f1 --- /dev/null +++ b/bridge-history-api/internal/orm/migrate/migrations/00003_bridge_batch_deposit_event_v2.sql @@ -0,0 +1,38 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE bridge_batch_deposit_event_v2 +( + id BIGSERIAL PRIMARY KEY, + token_type SMALLINT NOT NULL, + sender VARCHAR NOT NULL, + batch_index BIGINT DEFAULT NULL, + token_amount VARCHAR NOT NULL, + fee VARCHAR NOT NULL, + l1_token_address VARCHAR DEFAULT NULL, + l2_token_address VARCHAR DEFAULT NULL, + l1_block_number BIGINT DEFAULT NULL, + l2_block_number BIGINT DEFAULT NULL, + l1_tx_hash VARCHAR DEFAULT NULL, + l1_log_index INTEGER DEFAULT NULL, + l2_tx_hash VARCHAR DEFAULT NULL, + tx_status SMALLINT NOT NULL, + block_timestamp BIGINT NOT NULL, + created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP(0) DEFAULT NULL +); + +CREATE UNIQUE INDEX idx_l1hash_l1logindex ON bridge_batch_deposit_event_v2 (l1_tx_hash, l1_log_index); +CREATE INDEX IF NOT EXISTS idx_bbde_batchidx_sender ON bridge_batch_deposit_event_v2 (batch_index, sender); +CREATE INDEX IF NOT EXISTS idx_bbde_l1_block_number ON bridge_batch_deposit_event_v2 (l1_block_number DESC); +CREATE INDEX IF NOT EXISTS idx_bbde_l2_block_number ON bridge_batch_deposit_event_v2 (l2_block_number DESC); +CREATE INDEX IF NOT EXISTS idx_bbde_l1_tx_hash ON bridge_batch_deposit_event_v2 (l1_tx_hash DESC); +CREATE INDEX IF NOT EXISTS idx_bbde_l2_tx_hash ON bridge_batch_deposit_event_v2 (l2_tx_hash DESC); +CREATE INDEX IF NOT EXISTS idx_bbde_sender_block_timestamp ON bridge_batch_deposit_event_v2 (sender, block_timestamp DESC); + +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE IF EXISTS bridge_batch_deposit_event_v2; +-- +goose StatementEnd diff --git a/bridge-history-api/internal/route/route.go b/bridge-history-api/internal/route/route.go index 9123d6ca4..7e3159da4 100644 --- a/bridge-history-api/internal/route/route.go +++ b/bridge-history-api/internal/route/route.go @@ -27,9 +27,9 @@ func Route(router *gin.Engine, conf *config.Config, reg prometheus.Registerer) { r := router.Group("api/") - r.GET("/txs", api.HistoryCtrler.GetTxsByAddress) - r.GET("/l2/withdrawals", api.HistoryCtrler.GetL2WithdrawalsByAddress) - r.GET("/l2/unclaimed/withdrawals", api.HistoryCtrler.GetL2UnclaimedWithdrawalsByAddress) + r.GET("/txs", api.TxsByAddressCtl.GetTxsByAddress) + r.GET("/l2/withdrawals", api.L2WithdrawalsByAddressCtl.GetL2WithdrawalsByAddress) + r.GET("/l2/unclaimed/withdrawals", api.L2UnclaimedWithdrawalsByAddressCtl.GetL2UnclaimedWithdrawalsByAddress) - r.POST("/txsbyhashes", api.HistoryCtrler.PostQueryTxsByHashes) + r.POST("/txsbyhashes", api.TxsByHashesCtl.PostQueryTxsByHashes) } diff --git a/bridge-history-api/internal/types/events.go b/bridge-history-api/internal/types/events.go new file mode 100644 index 000000000..40c84044f --- /dev/null +++ b/bridge-history-api/internal/types/events.go @@ -0,0 +1,93 @@ +package types + +// TxStatusType represents the status of a transaction. +type TxStatusType int + +// Constants for TxStatusType. +const ( + // TxStatusTypeSent is one of the initial statuses for cross-chain messages. + // It is used as the default value to prevent overwriting the transaction status in scenarios where the message status might change + // from a later status (e.g., relayed) back to "sent". + // Example flow (L1 -> L2 message, and L1 fetcher is slower than L2 fetcher): + // 1. The relayed message is first tracked and processed, setting tx_status to TxStatusTypeRelayed. + // 2. The sent message is later processed (same cross-chain message), the tx_status should not over-write TxStatusTypeRelayed. + TxStatusTypeSent TxStatusType = iota + TxStatusTypeSentTxReverted // Not track message hash, thus will not be processed again anymore. + TxStatusTypeRelayed // Terminal status. + // TxStatusTypeFailedRelayed Retry: this often occurs due to an out of gas (OOG) issue if the transaction was initiated via the frontend. + TxStatusTypeFailedRelayed + // TxStatusTypeRelayTxReverted Retry: this often occurs due to an out of gas (OOG) issue if the transaction was initiated via the frontend. + TxStatusTypeRelayTxReverted + TxStatusTypeSkipped + TxStatusTypeDropped // Terminal status. + + // TxStatusBridgeBatchDeposit use deposit token to bridge batch deposit contract + TxStatusBridgeBatchDeposit + // TxStatusBridgeBatchDistribute bridge batch deposit contract distribute tokens to user success + TxStatusBridgeBatchDistribute + // TxStatusBridgeBatchDistributeFailed bridge batch deposit contract distribute tokens to user failed + TxStatusBridgeBatchDistributeFailed +) + +// TokenType represents the type of token. +type TokenType int + +// Constants for TokenType. +const ( + TokenTypeUnknown TokenType = iota + TokenTypeETH + TokenTypeERC20 + TokenTypeERC721 + TokenTypeERC1155 +) + +// MessageType represents the type of message. +type MessageType int + +// Constants for MessageType. +const ( + MessageTypeUnknown MessageType = iota + MessageTypeL1SentMessage + MessageTypeL2SentMessage + MessageTypeL1BatchDeposit +) + +// RollupStatusType represents the status of a rollup. +type RollupStatusType int + +// Constants for RollupStatusType. +const ( + RollupStatusTypeUnknown RollupStatusType = iota + RollupStatusTypeFinalized // only batch finalized status is used. +) + +// MessageQueueEventType represents the type of message queue event. +type MessageQueueEventType int + +// Constants for MessageQueueEventType. +const ( + MessageQueueEventTypeUnknown MessageQueueEventType = iota + MessageQueueEventTypeQueueTransaction + MessageQueueEventTypeDequeueTransaction + MessageQueueEventTypeDropTransaction +) + +// BatchStatusType represents the type of batch status. +type BatchStatusType int + +// Constants for BatchStatusType. +const ( + BatchStatusTypeUnknown BatchStatusType = iota + BatchStatusTypeCommitted + BatchStatusTypeReverted + BatchStatusTypeFinalized +) + +// UpdateStatusType represents the whether batch info is updated in message table. +type UpdateStatusType int + +// Constants for UpdateStatusType. +const ( + UpdateStatusTypeUnupdated UpdateStatusType = iota + UpdateStatusTypeUpdated +) diff --git a/bridge-history-api/internal/types/types.go b/bridge-history-api/internal/types/schema.go similarity index 93% rename from bridge-history-api/internal/types/types.go rename to bridge-history-api/internal/types/schema.go index 554e4e00c..22930aad0 100644 --- a/bridge-history-api/internal/types/types.go +++ b/bridge-history-api/internal/types/schema.go @@ -4,8 +4,6 @@ import ( "net/http" "github.com/gin-gonic/gin" - - "scroll-tech/bridge-history-api/internal/orm" ) const ( @@ -79,17 +77,18 @@ type TxHistoryInfo struct { ReplayTxHash string `json:"replay_tx_hash"` RefundTxHash string `json:"refund_tx_hash"` MessageHash string `json:"message_hash"` - TokenType orm.TokenType `json:"token_type"` // 0: unknown, 1: eth, 2: erc20, 3: erc721, 4: erc1155 + TokenType TokenType `json:"token_type"` // 0: unknown, 1: eth, 2: erc20, 3: erc721, 4: erc1155 TokenIDs []string `json:"token_ids"` // only for erc721 and erc1155 TokenAmounts []string `json:"token_amounts"` // for eth and erc20, the length is 1, for erc721 and erc1155, the length could be > 1 - MessageType orm.MessageType `json:"message_type"` // 0: unknown, 1: layer 1 message, 2: layer 2 message + MessageType MessageType `json:"message_type"` // 0: unknown, 1: layer 1 message, 2: layer 2 message L1TokenAddress string `json:"l1_token_address"` L2TokenAddress string `json:"l2_token_address"` BlockNumber uint64 `json:"block_number"` - TxStatus orm.TxStatusType `json:"tx_status"` // 0: sent, 1: sent failed, 2: relayed, 3: failed relayed, 4: relayed reverted, 5: skipped, 6: dropped + TxStatus TxStatusType `json:"tx_status"` // 0: sent, 1: sent failed, 2: relayed, 3: failed relayed, 4: relayed reverted, 5: skipped, 6: dropped CounterpartChainTx *CounterpartChainTx `json:"counterpart_chain_tx"` ClaimInfo *ClaimInfo `json:"claim_info"` BlockTimestamp uint64 `json:"block_timestamp"` + BatchDepositFee string `json:"batch_deposit_fee"` // only for bridge batch deposit } // RenderJSON renders response with json diff --git a/common/version/version.go b/common/version/version.go index f8a6af8bf..088732f69 100644 --- a/common/version/version.go +++ b/common/version/version.go @@ -5,7 +5,7 @@ import ( "runtime/debug" ) -var tag = "v4.4.5" +var tag = "v4.4.6" var commit = func() string { if info, ok := debug.ReadBuildInfo(); ok {