{"id":20189877,"url":"https://github.com/crossbell-box/bridge","last_synced_at":"2025-10-20T08:17:06.157Z","repository":{"id":65230570,"uuid":"585068813","full_name":"Crossbell-Box/bridge","owner":"Crossbell-Box","description":null,"archived":false,"fork":false,"pushed_at":"2024-04-09T09:19:19.000Z","size":659,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-13T18:50:13.305Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Crossbell-Box.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-01-04T08:35:19.000Z","updated_at":"2023-01-31T11:14:45.000Z","dependencies_parsed_at":"2023-12-15T09:31:29.770Z","dependency_job_id":"d57120ba-d705-468e-b5e1-8ad727e6fa8f","html_url":"https://github.com/Crossbell-Box/bridge","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Crossbell-Box%2Fbridge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Crossbell-Box%2Fbridge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Crossbell-Box%2Fbridge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Crossbell-Box%2Fbridge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Crossbell-Box","download_url":"https://codeload.github.com/Crossbell-Box/bridge/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241624798,"owners_count":19992941,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-14T03:39:22.939Z","updated_at":"2025-10-20T08:17:06.075Z","avatar_url":"https://github.com/Crossbell-Box.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Bridge v2\n\nBridge v2 is the second version of [bridge](https://github.com/axieinfinity/bridge) which is used for validators\nand relayers to listen to events triggered from Ronin or other chains connected to Ronin (eg: Ethereum). There are two mains\nfeatures:\n- Voting and relaying bridge operators (it currently changes in every period - `1 day`).\n- Listen to deposit and withdraw request on Ronin/Ethereum and relay it to the others.\n\n# How to run\n## Requirements\n\n- Ronin RPC URL is used to listen/trigger events from Ronin chain\n- Ethereum RPC URL (Alchemy, Infura, etc.) is used to listen/trigger events from Ethereum\n- Postgres DB to store events and tasks\n\n## Install and run\n### Using docker\nThere is a `docker-compose.yaml` file in `docker` directory. Modify `.env` file and run `bridge` with the following command\n```\ndocker-compose -f docker/docker-compose.yaml --env-file .env up -d\n```\n### Manually\n\nInstall PostgreSQL database, create `bridge` database\n\nbuild and install bridge\n\n```\nmake bridge\n```\n\nthen run Bridge with\n\n```\nbridge --config \u003cpath-to-config\u003e\n```\n\n## Configuration\nThe config file can be found in the `config` directory. There are 2 main components in the configuration: listeners and database\n\n### listeners (object)\nList all chains that Bridge is listening to. Each name reflects a specific function defined [here](https://github.com/axieinfinity/bridge-v2/blob/master/internal/init_listeners.go).\n\nFor example `Ronin` reflects with function `InitRonin`\n\nTherefore, do not change the name, otherwise, the program cannot run properly.\n\n#### 1. chainId:\n- type: `hex string`\n\nChain's identity (ronin: 0x7e4, ethereum: 0x1)\n\n#### 2. rpcUrl:\n- type: `string`\n\nRPC URL of chain which is used to query new events or submit transactions to.\n\n#### 3. blockTime:\n- type: `number`\n- unit: `seconds`\n\nThe time of a new block is generated which is used periodically to listen to new events from the new block,\n\n#### 4. safeBlockRange: \n- type: `number`\n- unit: `blocks`\n\nSafe block range which guarantees that reorg cannot happen.\n\n#### 5. maxTasksQuery:\n- type: `number`\n\nMaximum number of pending/processing tasks queried from the database\n\n#### 6. transactionCheckPeriod:\n- type: `number`\n- unit: `seconds`\n\nPeriod of checking whether a transaction is mined or not by querying its transaction's receipt. If a receipt is found,\nit will try 3 more times to ensure the transaction is not replaced because of reorg.\n\n#### 7. secret:\n- type: `object`\n\nStores private key of validator and relayer. These fields can be empty and passed via environment variables\nthrough 2 variables: `RONIN_VALIDATOR_KEY`, `RONIN_RELAYER_KEY` and Ethereum are: `ETHEREUM_VALIDATOR_KEY`, `ETHEREUM_RELAYER_KEY`\n##### syntax: `\u003ckey\u003e`\n##### example: `xxxx4563e6591c1eba4b932a3513006cb5bcd1a6f69c32295dxxxx`\nIf KMS is used, set these environments\n- `RONIN_VALIDATOR_KMS_KEY_TOKEN_PATH`\n- `RONIN_VALIDATOR_KMS_SSL_CERT_PATH`\n- `RONIN_VALIDATOR_KMS_SERVER_ADDR`\n- `RONIN_VALIDATOR_KMS_SOURCE_ADDR`\n- `RONIN_VALIDATOR_KMS_SIGN_TIMEOUT`\n\nReplace `RONIN` with `ETHEREUM`, `VALIDATOR` with `RELAYER` when needed\n\nThis is the sample secret's config\n```json5\n{\n  \"secret\": {\n    \"validator\": {\n      \"kmsConfig\": {\n        \"keyTokenPath\": \"./key\",\n        \"sslCertPath\": \"./ssl.crt\",\n        \"kmsServerAddr\": \"127.0.0.1:1234\",\n        \"kmsSourceAddr\": \":5000\",\n        \"signTimeout\": 3000\n      }\n    },\n    \"relayer\": {\n      \"plainPrivateKey\": \"\" // Raw private key\n    }\n  }\n}\n```\n\n#### 8. fromHeight:\n- type: `number`\n- unit: `blocks`\n\nInitially, Bridge uses this property to load data from this block. After that, Bridge will store the latest processed block in `processed_block` table and use the value from this table to continue.\n\n#### 9. processWithinBlocks:\n- type: `number`\n- unit: `blocks`\n\nThis property guarantees that Bridge does not process too far. Specifically, when `latestBlock - processWithinBlocks \u003e fromHeight`, bridge `latestBlock - processWithinBlocks` instead of `fromHeight` to process.\n\n#### 10. contracts\n- type: `record\u003cstring, address\u003e`\n\nStores a map (pair) of names and contact addresses, which can be used during processing tasks or jobs of a listener. For example, in `Ronin` listener, 2 contracts which are Ronin Gateway contract (at `Gateway`) and Ethereum Gateway contract (at `EthGateway`) are used:\n```json\n{\n  \"Gateway\": \"0x03d1F13c7391F6B5A651143a31034cf728A93694\",\n  \"EthGateway\": \"0x3b6371EB912bFd5C0E249A16000ffbC6B881555A\"\n}\n```\n\n#### 11. subscriptions\n- type: `object`\n\nIncludes all subscriptions bridge is observing in a listener. Each subscription contains the subscription name and subscription config.\n- `to`: Indicates receiver/contract address that bridge uses as one of conditions to trigger a subscription\n- `type`: There are 2 types, `0` is `transaction event` and `1` is `log's event`\n- `handler`: Define contract and event that we want to listen\n  - `contract`: Contract name. This must be defined on repo [Bridge Contracts](https://github.com/axieinfinity/bridge-contracts/blob/master/main.go#L13-L20)\n  - `name`: The event name\n- `callbacks`: List all callbacks function when data is decoded. This is a map (pair) where the key is listener's name and value is the function that is called in that listener. For example:\n\n```json5\n{\n  \"to\": \"0xA8D61A5427a778be28Bd9bb5990956b33385c738\",\n  \"type\": 1, // Listen log's event\n  \"handler\": {\n    \"contract\": \"RoninGateway\",\n    \"name\": \"MainchainWithdrew\"\n  },\n  \"callbacks\": {\n    \"Ronin\": \"StoreMainchainWithdrawCallback\"\n  }\n}\n```\n\nBridge will trigger the function `StoreMainchainWithdrawCallback` in `RoninListener`\n\n#### Example\nFor example, Bridge will listen to event `Welcomed` which is defined on contract `Hello` \nand submit the data to `HelloEth` contract via method `SubmitFromRonin`\n```json5\n{\n  \"MainchainWithdrewSubscription\": {\n    \"to\": \"0xA8D61A5427a778be28Bd9bb5990956b33385c738\",\n    \"type\": 1,\n    \"handler\": {\n      \"contract\": \"RoninGateway\", // The contract name, it must be defined on [Bridge Contracts](https://github.com/axieinfinity/bridge-contracts/blob/master/main.go#L13-L20) first.\n      \"name\": \"Welcome\" // The event is listening\n    },\n    \"callbacks\": {\n      \"Ronin\": \"WelcomeCallback\" // Execute the callback on Ronin chain\n    }\n  },\n}\n```\nOn `litenser/ronin.go` add the following method\n```go\npackage listener\n\nfunc (l *RoninListener) WelcomeCallback(fromChainId *big.Int, tx bridgeCore.Transaction, data []byte) error {\n\t// Unpack event data\n\troninEvent := new(hello.WelcomeEvent)\n\troninGatewayAbi, err := hello.GatewayMetaData.GetAbi()\n\n\t// Since the data argument was the log's marshalled in bytes, so it must be unmarshalled\n\t// before being use\n\tif err = l.utilsWrapper.UnpackLog(*roninGatewayAbi, roninEvent, \"Welcome\", data); err != nil {\n\t\treturn err\n\t}\n\n    chainId, err := l.GetChainID()\n  \n\t// Create a new task\n    t := \u0026models.Task{\n        ChainId:         hexutil.EncodeBig(chainId),\n        FromChainId:     hexutil.EncodeBig(fromChainId),\n        FromTransaction: tx.GetHash().Hex(),\n        Type:            task.WELCOME_TASK, // defined in task/main.go\n        Data:            common.Bytes2Hex(data),\n        Retries:         0,\n        Status:          task.STATUS_PENDING,\n        LastError:       \"\",\n        CreatedAt:       time.Now().Unix(),\n    }\n\t\n\t// Get the store API to save the task to database\n\treturn l.bridgeStore.GetTaskStore().Save(withdrawalTask)\n}\n```\nThen create a method `welcomeTask` in `task/task.go`\n```go\npackage task\n\nfunc (r *task) welcomeTask(task *models.Task) (doneTasks, processingTasks, failedTasks []*models.Task, tx *ethtypes.Transaction) {\n\t// create caller\n\ttransactor, err := helloEth.NewHelloEth(common.HexToAddress(r.contracts[HELLO_ETH_CONTRACT]), r.ethClient)\n\t\n    tx, err = r.util.SendContractTransaction(r.listener.GetValidatorSign(), r.chainId, func(opts *bind.TransactOpts) (*ethtypes.Transaction, error) {\n      return transactor.SubmitFromRonin(opts)\n    })\n\tdoneTasks = append(doneTasks, task)\n\treturn\n}\n```\nFinally, add it to `send` method:\n```go\npackage task\n\nfunc (r *task) send() {\n\tlog.Info(\"[task] Sending transaction\", \"type\", r.taskType)\n\tswitch r.taskType {\n\tcase VOTE_BRIDGE_OPERATORS_TASK:\n\t\tr.sendTransaction(r.voteBridgeOperatorsBySignature)\n\tcase RELAY_BRIDGE_OPERATORS_TASK:\n\t\tr.sendTransaction(r.relayBridgeOperators)\n\t}\n}\n\n```\n\n### Subscriptions\n\n#### MainchainWithdrewSubscription\n```mermaid\ngraph TD\n  tryBulkAcknowledgeMainchainWithdrew --\u003e |Emit| MainchainWithdrew\n  Bridge --\u003e |Listen| MainchainWithdrew\n  Bridge --\u003e A{Got event?}\n  A --\u003e |No| Bridge\n  A --\u003e |Yes| B[Store Receipt To Database]\n```\n#### WithdrawalRequestedSubscription\nRequest validators sign withdrawal transaction\n```mermaid\ngraph TD\n  bulkRequestWithdrawalFor --\u003e |Emit| WithdrawalRequested\n  Bridge --\u003e |Listen| WithdrawalRequested\n  Bridge --\u003e A{Got Event?}\n  A --\u003e |No| Bridge\n  A --\u003e |Yes| signWithdrawalSignatures\n  signWithdrawalSignatures --\u003e BulkSubmitWithdrawalSignatures\n```\n\n#### WithdrawalSignaturesRequestedSubscription\nRequest validators sign withdrawal transaction again\n```mermaid\ngraph TD\n  requestWithdrawalSignatures --\u003e |Emit| WithdrawalSignaturesRequested\n  Bridge --\u003e |Listen| WithdrawalRequested\n  Bridge --\u003e A{Got Event?}\n  A --\u003e |No| Bridge\n  A --\u003e |Yes| signWithdrawalSignatures\n  signWithdrawalSignatures --\u003e BulkSubmitWithdrawalSignatures\n```\n\n#### DepositRequestedSubscription\nWhen a user deposit ETH on Ethereum to contract. Bridge will listen this event and send it to Ronin\n```mermaid\ngraph TD\n  requestDepositFor --\u003e |Emit| DepositRequested\n  Bridge --\u003e |Listen| DepositRequested\n  Bridge --\u003e A{Got event?}\n  A --\u003e |No| Bridge\n  A --\u003e |Yes| sendDepositTransaction\n  sendDepositTransaction --\u003e B{Is Voted?}\n  B --\u003e |Yes| Done\n  B --\u003e |No| StoreProcessedReceipt\n  StoreProcessedReceipt --\u003e TryBulkDepositFor\n```\n#### WithdrewSubscription\nWhen a user withdraw ETH from contract. Bridge will listen this event and send it to Ronin\n```mermaid\ngraph TD\n  unlockWithdrawal --\u003e |Emit| Withdrew\n  submitWithdrawal --\u003e |Emit| Withdrew\n  Bridge --\u003e |Listen| Withdrew\n  Bridge --\u003e A{Got event?}\n  A --\u003e |No| Bridge\n  A --\u003e |Yes| MainchainWithdrewVoted\n  MainchainWithdrewVoted --\u003e B{Is Voted?}\n  B --\u003e |Yes| Done\n  B --\u003e |No| TryBulkAcknowledgeMainchainWithdrew\n```\n\n#### BridgeOperatorSetUpdatedSubscription\nAt the end of each period, validators call `wrapUpEpoch` of `ValidatorSet` contract to update list validator set.\nIt emits an event `BridgeOperatorSetUpdated(uint256 period, []address operators)`. All trusted nodes must listen this\nevent, vote by signing typed data and submit it to `RoninGovernanceAdmin` contract. \n```mermaid\ngraph TD\n  Validator --\u003e|Call| WrapUpEpoch\n  WrapUpEpoch --\u003e|Emit| BridgeOperatorSetUpdated\n  Bridge --\u003e |Listen| BridgeOperatorSetUpdated\n  Bridge --\u003e E{Got an event}\n  E --\u003e |Yes| BridgeOperatorsVoted\n  E --\u003e |No| Bridge\n  BridgeOperatorsVoted --\u003e A{Submitted}\n  A --\u003e |No| VoteBridgeOperatorsBySignatures\n  A --\u003e |Yes| Done\n```\n\n#### BridgeOperatorsApprovedSubscription\nAfter trusted nodes submitted vote's signature to `RoninGovernanceAdmin`. Relayer needs to \ncall `GetAllTrustedOrganizations` to get all trusted nodes, sort it as ascending. Then call `GetBridgeOperatorVotingSignatures`\nto get a list signatures that submitted on Ronin. Finally, relayer submits these signatures to\n`MainchainGovernanceAdmin` through `RelayBridgeOperators` method.\n```mermaid\ngraph TD\n  VoteBridgeOperatorsBySignatures --\u003e |Emit| BridgeOperatorsApproved\n  Bridge --\u003e |Listen| BridgeOperatorsApproved\n  Bridge --\u003e A{Got event}\n  A --\u003e |Yes| GetAllTrustedOrganizations\n  A --\u003e |No| Bridge\n  GetAllTrustedOrganizations --\u003e GetBridgeOperatorVotingSignatures\n  GetBridgeOperatorVotingSignatures --\u003e RelayBridgeOperators\n```\n\n### Database\nDatabase configuration is defined within the key `database`. Basic properties include host, port, user, password and dbName.\n\n```json5\n{\n  \"database\": {\n    \"host\": \"localhost\", // Database host name\n    \"user\": \"postgres\", // Database username\n    \"password\": \"example\", // Database password\n    \"dbName\": \"bridge\", // Database name\n    \"port\": 5432 // Database port\n  }\n}\n```\n\n## Contribution\n\nThank you for considering helping out with the source code! We welcome contributions\nfrom anyone on the internet, and are grateful for even the smallest of fixes!\n\nIf you'd like to contribute to Bridge, please fork, fix, commit and send a pull request\nfor the maintainers to review and merge into the main code base. If you wish to submit\nmore complex changes though, please check up with the core devs first issues on Github\nto ensure those changes are in line with the general philosophy of the project and/or get\nsome early feedback which can make both your efforts much lighter as well as our review\nand merge procedures quick and simple.\n\nPlease make sure your contributions adhere to our coding guidelines:\n\n* Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting)\n  guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).\n* Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary)\n  guidelines.\n* Pull requests need to be based on and opened against the `master` branch.\n* Commit messages should be prefixed with the package(s) they modify.\n  * E.g. \"fix: make trace configs optional\"\n\nPlease see the [Developers' Guide](https://geth.ethereum.org/docs/developers/devguide)\nfor more details on configuring your environment, managing project dependencies, and\ntesting procedures.\n\n## License\n\nBridge library and binaries (i.e. all code outside and inside of the `cmd` directory) is licensed under the\n[Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0),\nalso included in our repository in the `COPYING` file.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrossbell-box%2Fbridge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcrossbell-box%2Fbridge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrossbell-box%2Fbridge/lists"}