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/releasing
process 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
UI
that manages all of the user's interactions and provides an easy and smooth UX. - The
Smart contracts
which are the backbone of all transactions. OpenZeppelin Defender
which 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 minting
in 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
miniting
andreleasing
methods ? - How to avoid showing the
webhook
url 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 Relayer
s 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 keys
are 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
: aUNIX
timestamp of when the transaction has been started by the front-endData
: the data needed to be sent to thewebhook
in 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 mentionedtimestamp
anddata
(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/AimBridge
Go to the project directory
cd my-project
Install dependencies
npm install
#or
yarn install
Start the server
npm run dev
#or
yarn dev