AimBridge is a proof of concept for a EVM token-bridge developed that uses OpenZeppelin Defender in order to manage the transactions between two networks.
- Transfer tokens from source network to target network, by generating a new Wrapper Token in the target network.
- Reverse the wrapping process, by burning the warpped token in the source network and releasing locked tokens in the target network.
- Display history of user's transactions using The Graph .
- Streamline the
minting/releasingprocess by implementingOpenZeppelin Defender, to provide a smoother and easier UX. (This is highly experimental and not suggested in production).
The bridge is has three important components:
- the
UIthat manages all of the user's interactions and provides an easy and smooth UX. - The
Smart contractswhich are the backbone of all transactions. OpenZeppelin Defenderwhich are a very important component in this project, and what makes the UX much better.
This is a simplified version of how the whole system works.
The application detects all of the ERC20 tokens a users has, and allows them to select any of them to bridge to the target network.
After the users select their token, the amount is then transfered from the user's address into the bridge's address and locked there.
Once this process is complete, a webhook using OpenZeppelin AutoTask is called with the needed data in order to start the mintingin the target network.
This is done in order to ensure the transaction on target network is executed automatically, without the user needing to switch networks or claim later on.
Here's is a flowchart that explains the same details:

While OpenZeppelin AutoTask and OpenZeppelin Relayer help streamline the process, there are many security issues that arise.
- Can anyone call the
minitingandreleasingmethods ? - How to avoid showing the
webhookurl to users ? - How to secure a successful transaction against
replay attacks?
In order to solve the first issue, Roles were implemented into the bridge smart contracts, which makes OpenZeppelin Relayers perfect for this.
By giving the Relayer account a role that allows them and only them to call the methods, we prevent regular users from causing issues.
And since the relayes keysare injected automatically into the OpenZeppelin AutoTask this makes it easier.
In order to avoid the WebHook from being overwhelmed or attacked, it the POST request is abstracted by a NextJS api route.
The route is the only way to communicate with the WebHook.
And the route is also secure with a secret key, any request to that route would fail unless the key is provided.
Replay attacks are the biggest threat to this architecture, a Replay Attack is when an attacker intercepts a communication, in our case the webhook call and delays it or repeat the successful transaction again.
It is apparent that if an attacker can repeat a successful transaction again, one could mine unlimited amount of the same token over and over again, or release the total balance of the bridge contract.
In order to avoid that a security mesure has been put in place.
timestamp + hash:
Any transaction sent to the webhook would consist of three things,
Timestamp: aUNIXtimestamp of when the transaction has been started by the front-endData: the data needed to be sent to thewebhookin order for it to carry out the command properly.Hash: this is a hash generated using a private key, its a combination of the previously mentionedtimestampanddata(h(timestamp+data)).
Once the POST request is sent to the webhook, if first checks the time passed from the timestamp it received and the current timestamp. If the difference between the two is bigger than a certain interval the request is automatically rejected.
ex: timestamp-webhook - timestamp-front-end > 5 minutes { reject }
This is just an example and not how the actual thing is coded.
Next, the webhook generates a hash using the same private key as the front-end, if the newly generated hash is the same with the one sent by the front-end it means the data has not been tampered with.
ex : 5a6b9c05466f6826530c379c2d1ad87b === 5a6b9c05466f6826530c379c2d1ad87b Passes
5a6b9c05466f6826530c379c2d1ad87b === 5a667adbd4dfcaae3c8f86a835b8b54f REJECTED
a change in the original timestamp or the data would also cause the hash to change, which leads to the request being rejected.
Even though there are security measures implemented , this is just a proof of concept and is not advised to be deployed to production.
Please make sure that your system is well secured before deploying as it can cause major loses for both the service and users.
To run this project, you will need to add the following environment variables to your .env file
#Alchemy API key
NEXT_PUBLIC_ALCHEMY_KEY=
#Alchemy Goerli HTTPS URL
NEXT_PUBLIC_ALCHEMY_GOERLI=
#Alchemy Mumbai HTTPS URL
NEXT_PUBLIC_ALCHEMY_MUMBAI=
#Web-hook urls provided by OpenZeppelin Defender Autotask
NEXT_PUBLIC_MUMBAI_WEBHOOK =
NEXT_PUBLIC_GOERLI_WEBHOOK =
#Secret key used for hashing
NEXT_PUBLIC_WEBHOOK_SECRET=
#Secret key used for securing API route
NEXT_PUBLIC_API_SECRET_KEY=
If you want to use your own deployed smart contrants you should change the addresses in src/utils/chain-info.ts
export const chainInfo: Record<string, Chain> = {
5: {
name: "Goerli",
contract: YOUR CONTRACT ADDRESS,
alchemyUrl: process.env.NEXT_PUBLIC_ALCHEMY_GOERLI as string,
webHookUrl: process.env.NEXT_PUBLIC_GOERLI_WEBHOOK as string,
explorer: "https://goerli.etherscan.io/tx/",
subgraph: YOUR SUBGRAPH ADDRESS,
},
80001: {
name: "Mumbai",
contract: YOUR CONTRACT ADDRESS,
alchemyUrl: process.env.NEXT_PUBLIC_ALCHEMY_MUMBAI as string,
webHookUrl: process.env.NEXT_PUBLIC_MUMBAI_WEBHOOK as string,
explorer: "https://mumbai.polygonscan.com/tx/",
subgraph: YOUR SUBGRAPH ADDRESS
},
};
Clone the project
git clone https://github.com/aimensahnoun/AimBridgeGo to the project directory
cd my-projectInstall dependencies
npm install
#or
yarn installStart the server
npm run dev
#or
yarn dev


