From Uniswap V2 to Swoop on Harmony

Swoop is a Uniswap V2 fork on Harmony.

Swoop started out as a simple internal hackathon project that eventually grew into a feature complete tech demo, spanning a total of 10 different implementation repositories.

The main goal of porting Uniswap V2 over to Harmony was to showcase how fast, responsive and cheap Uniswap can be on a blockchain with 5-second finality and extremely low transaction costs.

Project scope

The Uniswap V2 to Swoop migration includes a total of 10 implementation repositories.

Out of these 10 repositories, six repositories are directly forked from Uniswap:

UI/SDK:

Smart contracts:

Besides the six forked repositories, Swoop also contains four additional repositories:

UI/SDK:

Smart contracts:

Deployment:

Smart contracts

The original Uniswap V2 contracts were just slightly modified - during the process of porting Swoop we only needed to change some minor differences for customizing the LP token names and symbols as well as updating the init code hash for the UniswapV2Pair contract.

The changes occurred in the following contracts:

Core (uniswap/uniswap-v2-core -> harmony-one/swoop-core)

Repository: https://github.com/harmony-one/swoop-core​

UniswapV2ERC20.sol

Name and symbols have been changed from Uniswap V2 and UNI-V2 to Swoop and SWP respectively:

- string public constant name = 'Uniswap V2';
- string public constant symbol = 'UNI-V2';
+ string public constant name = 'Swoop';
+ string public constant symbol = 'SWP';

Periphery (uniswap/uniswap-v2-periphery -> harmony-one/swoop-periphery)

Repository: https://github.com/harmony-one/swoop-periphery​

UniswapV2Library.sol

Package reference was updated:

-import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
+import '@swoop-exchange/core/contracts/interfaces/IUniswapV2Pair.sol';

Init code hash was updated:

@@ -21,7 +22,7 @@
hex'ff',
factory,
keccak256(abi.encodePacked(token0, token1)),
- hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash
+ hex'e3c4d7c2f0f0eb6af0a666a9b54ea1196dd3676e4e4b696af853d8951f807cc5' // init code hash
))));
}

UniswapV2Router02.sol

Package references were updated:

-import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
-import '@uniswap/lib/contracts/libraries/TransferHelper.sol';
+import '@swoop-exchange/core/contracts/interfaces/IUniswapV2Factory.sol';
+import '@swoop-exchange/lib/contracts/libraries/TransferHelper.sol';

The above were the only changes that needed to be carried out on the smart contract level when porting the original Uniswap V2 contracts since Harmony is fully EVM-compatible.

For detailed diffs between the Uniswap V2 contracts and the Swoop contracts, please see more details here.

Misc contracts (non-Uniswap)

Due to migrating Uniswap to a new chain we also had to port some peripheral (non Uniswap) related contracts:

The two contracts above are required to be deployed in order to fully be able to fork Uniswap to Harmony.

The entire deployment process for deploing Swoop on Harmony has been documented here.

dApp / Interface (UI) / SDK:s & libraries

The major work with porting Swoop happened at the UI and SDK/library level.

​Porting the interface resulted in by far the most amount of work compared to all of the other repositories.

Ethers.js -> harmony-js/core

Uniswap V2 relies on ethers.js which currently does not work with Harmony due to Harmony being a sharded blockchain. Harmony is actively working on a wrapper library that will hopefully be available soon.

Due to the usage of ethers.js all smart contract interactions had to be changed from an ethers.js-style interaction flow to use harmony-js/core instead. Harmony-js/core (JS SDK) is partially designed after web3.js, so if Uniswap V2 would've been implemented using web3.js the changes wouldn't have been as drastic.

Due to the migration from ethers.js to harmony-js/core, all of the smart contract interactions had to be changed from e.g:

return tokenContract
.approve(spender, useExact ? amountToApprove.raw.toString() : MaxUint256, {
gasLimit: calculateGasMargin(estimatedGas)
})
.then((response: TransactionResponse) => {
addTransaction(response, {
summary: 'Approve ' + amountToApprove.currency.symbol,
approval: { tokenAddress: token.address, spender: spender }
})
})
.catch((error: Error) => {
console.debug('Failed to approve token', error)
throw error
})

To e.g:

return tokenContract.methods
.approve(spender, useExact ? amountToApprove.raw.toString() : MaxUint256.toString()).send(gasOptions)
.then((response: any) => {
addTransaction(response, {
summary: 'Approve ' + amountToApprove.currency.symbol,
approval: { tokenAddress: token.address, spender: spender }
})
})
.catch((error: Error) => {
setApproveTxSent( false)
console.debug('Failed to approve token', error)
throw error
})

Due to replacing ethers.js to harmony-js/core we also ran into some issues with various parameter issues (numbers sent as string vs hex etc.) that has since been patched in harmony-js/core.

Wallets - MetaMask/web3-react -> custom OneWallet & MathWallet code

Harmony isn't currently compatible with MetaMask, neither do we currently have our own version of web3-react (but it's currently being worked on).

Because of this we had to replace the web3-react code in Uniswap with custom wallet connectors for OneWallet & MathWallet.

We're hoping to have our own version of web3-react available soon.

Miscellaneous changes

Besides the work on replacing the ethers.js smart contract interaction code with the harmony-js/core equivalent version as well as replacing web3-react with custom wallet code, we also had to change the following in the interface code base:

  • Token references (both addresses and names)

  • ChainID references

  • Package references

  • Parameter and result formatting

  • Certain interaction flows (which were tailored to the use of MetaMask and similar wallets)

  • + a bunch of other things - see this commit comparison for more details.

SDK (uniswap/uniswap-sdk -> harmony-one/swoop-sdk)

​Porting the Uniswap SDK to Swoop SDK mostly revolved around replacing the deployed factory and router addresses, changing references from ETHER to HARMONY, using @harmony-js/core's ChainID for ChainID references as well as replacing WETH references and addresses with the WONE counterpart.

​Porting the Uniswap default token list was mostly about replacing Ethereum tokens to tokens deployed on Harmony.

Utils (not forked)

The swoop-utils repository was created in order to externalize the wallet connectors as well as a wrapper client library for interacting with harmony-js/core.

CLI (not forked)

The swoop-cli repository was created in order to perform ad-hoc testing of the factory and router contracts. This was extremely helpful when debugging certain edge cases, both on a contract and a interface level.

Deployment (not forked)

The original Uniswap V2 code base does not include code to deploy the smart contracts. Since Harmony also utilizes a custom deployment setup, using either Truffle with a custom provider or directly using harmony-js/core, the swoop-deployment repository was also created.

Besides the required deployment scripts needed to deploy the project the repository also features a step-by-step guide for how to deploy the entire protocol - from smart contracts to the UI.

Lessons learned

During the process of porting Uniswap to Harmony we learned how fast Uniswap can be with 5-second finality and how cheap it is for users to interact with the protocol.

We learned that our tooling is robust enough to port a dApp like Uniswap, but that we need to work towards a more feature complete toolset with wrappers for ethers.js and web3-react.

Due to our work on porting Uniswap we've realized the need for accelerating this process and we're currently actively working on these tools in order to provide an awesome dApp development environment for developers looking to build on Harmony.

​

​

​