Route2 Web3 week 6-8
Hey everyone! It's been a while, and it has been a roller coaster ride indeed. The past few weeks at EIF were the build phase, and it was all about implementing the different aspects of my project based on Wallet Security using MPC (multi-party computation) and ZKP (zero-knowledge proofs). It proposes a key recovery system using multi-party computation and zero-knowledge proofs to provide wallet security. The past few weeks were all about implementing Lagrange Interpolation or Gaussian e...
Route2 Web3: week4
This week was all about understanding how multisigs and SVG NFTs work. https://github.com/route-2/challenge-5-multisig Learnt about meta transactions and off-chain signature-based shared wallet amongst different signers.//generates a unique hash for each transaction, function getTransactionHash(uint256 _nonce, address to, uint256 value, bytes memory data) public view returns (bytes32) { return keccak256(abi.encodePacked(address(this), chainId, _nonce, to, value, data)); } // executing a trans...
Route2 Web3: week5
This week began with the build phase at EIF, and I worked on getting the project proposal ready. I was very interested in implementing something related to cryptography, so I came up with the idea of using MPC (Multiparty Computation) and ZK (Zero Knowledge) protocols. Before proceeding with this idea, I decided to revise my cryptography basics and deep dive into MPC to improve my understanding of it. If you’re wondering what MPC is, MPC stands for Multiparty Computation. Multiparty computati...
<100 subscribers
Route2 Web3 week 6-8
Hey everyone! It's been a while, and it has been a roller coaster ride indeed. The past few weeks at EIF were the build phase, and it was all about implementing the different aspects of my project based on Wallet Security using MPC (multi-party computation) and ZKP (zero-knowledge proofs). It proposes a key recovery system using multi-party computation and zero-knowledge proofs to provide wallet security. The past few weeks were all about implementing Lagrange Interpolation or Gaussian e...
Route2 Web3: week4
This week was all about understanding how multisigs and SVG NFTs work. https://github.com/route-2/challenge-5-multisig Learnt about meta transactions and off-chain signature-based shared wallet amongst different signers.//generates a unique hash for each transaction, function getTransactionHash(uint256 _nonce, address to, uint256 value, bytes memory data) public view returns (bytes32) { return keccak256(abi.encodePacked(address(this), chainId, _nonce, to, value, data)); } // executing a trans...
Route2 Web3: week5
This week began with the build phase at EIF, and I worked on getting the project proposal ready. I was very interested in implementing something related to cryptography, so I came up with the idea of using MPC (Multiparty Computation) and ZK (Zero Knowledge) protocols. Before proceeding with this idea, I decided to revise my cryptography basics and deep dive into MPC to improve my understanding of it. If you’re wondering what MPC is, MPC stands for Multiparty Computation. Multiparty computati...
Share Dialog
Share Dialog
This week was the defi week and we got to work on building a decentralised exchange and also on layer-2 solutions like state channels.
https://speedrunethereum.com/builders/0xFEa64Acb621B94a6Bc9eAbc966427B0C3687B22A
To get an understanding how DEX really works I went through uniswap v1,v2 and v3. The newest v3 upgrade includes concentrated liquidity,Multiple fee tiers, and Customisable trading strategies.It also includes security features such as timelocks and access controls to prevent unauthorised access or manipulation of the contract.
Uniswap v3 has five fee tiers: 0.05%, 0.3%, 1%, 3%, and 5%.
The fee tier is determined by the distance between the two ticks that define the liquidity range of the position. Positions with a narrower tick range (closer ticks) earn a lower fee, while positions with a wider tick range (farther ticks) earn a higher fee.
// Allows a user to add liquidity to the pool by depositing an equal value of two tokens in a specific price range defined by `tickLower` and `tickUpper`
function mint(
address recipient,
int24 tickLower,
int24 tickUpper,
uint128 amount,
bytes calldata data
) external override lock returns (uint256 amount0, uint256 amount1) {
require(amount > 0);
(, int256 amount0Int, int256 amount1Int) = _modifyPosition(
ModifyPositionParams({
owner: recipient,
tickLower: tickLower,
tickUpper: tickUpper,
liquidityDelta: int256(amount).toInt128()
})
);
amount0 = uint256(amount0Int);
amount1 = uint256(amount1Int);
uint256 balance0Before;
uint256 balance1Before;
if (amount0 > 0) balance0Before = balance0();
if (amount1 > 0) balance1Before = balance1();
IUniswapV3MintCallback(msg.sender).uniswapV3MintCallback(amount0, amount1, data);
if (amount0 > 0) require(balance0Before.add(amount0) <= balance0(), 'M0');
if (amount1 > 0) require(balance1Before.add(amount1) <= balance1(), 'M1');
emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1);
}
In the DEX challenge we understood how AMM works, considering x * y = k
(amount of ETH in DEX ) * ( amount of tokens in DEX ) = k
The k only changes as liquidity is added.
How does AMM really work?
Consider An AMM has a pool of two assets, such as ETH and USDT.
Each asset is assigned a weight, which determines its relative value in the pool. For example, if ETH has a weight of 0.6 and USDT has a weight of 0.4, then ETH is considered to be worth more than USDT in the pool.
The product of the two assets' balances in the pool is always kept constant. So, if the pool starts with 1 ETH and 100 USDT, the product is 1*100=100.
When a trader wants to buy or sell one of the assets, they trade with the AMM instead of another person. The AMM uses the constant product formula to determine the new price of the asset being bought or sold.
For example, if a trader wants to buy 0.1 ETH with their USDT, the AMM will calculate the new balances of ETH and USDT that will keep the product constant. Let's say the new ETH balance is 1.1 and the new USDT balance is 90.91. The new price of ETH is then calculated by dividing the new USDT balance by the new ETH balance, which gives us 90.91/1.1=82.64 USDT per ETH.
The AMM charges a small fee for each trade, which is added to the liquidity pool. This incentivises liquidity providers to add funds to the pool and earn a share of the trading fees.
The functions for swapping from each asset to the other are ethToToken() and tokenToEth(). Each of these functions calculates the resulting amount of output asset using the price function that looks at the ratio of the reserves vs the input asset.
The deposit() function is used to deposit ETH and a corresponding ERC20 token into liquidity pool, which creates liquidity tokens in return. It calculates the amount of tokens to deposit based on the current reserve ratio of ETH and the ERC20 token in the pool, and then transfers the tokens from the sender to the liquidity pool contract. The function also mints new liquidity tokens for the sender and updates the total liquidity of the pool.
The withdraw() function is used to withdraw liquidity from the liquidity pool. The function takes as input the “amount of liquidity tokens” to withdraw and returns the corresponding “amount of ETH and ERC20 tokens”. The function calculates the amounts to withdraw based on the current reserve ratios and updates the sender's liquidity balance and the total liquidity of the pool accordingly. Finally, the function transfers the ETH and ERC20 tokens to the sender's address.
The function price() first includes 0.3% fee to the input token, and the 0.1% fee charged by the DEX on trades.
Watch this video for more understanding
function price(
uint256 xInput,
uint256 xReserves,
uint256 yReserves
) public view returns (uint256 yOutput) {
uint256 xInputWithFee = xInput.mul(997);
uint256 numerator = xInputWithFee.mul(yReserves);
uint256 denominator = (xReserves.mul(1000)).add(xInputWithFee);
return (numerator / denominator);
}
Challenges I faced during this challenge was to implement the AMM in code, and passing the testcases.
Before I go ahead with the state channels challenge, here’s an overview of what state channels are ? what is the need of state channels?
As said in this tweet Ethereum is not your grandpa’s database and you don’t store everything and anything onchain.
https://twitter.com/Kautukkundan/status/1626622768249962498?s=20
A number of approaches to scaling have been developed, collectively referred to as layer-2s (L2s). One of them being the State Channels.
In State Channels users agree on the initial state and multiple transactions are done off-chain and only the final state is updated on chain, in this way we save transaction fees and also the load on network.
They enable off-chain transactions between parties in a blockchain network. They involve opening a private communication channel between two parties and executing multiple transactions off-chain without the need to record them on the blockchain immediately.
In this challenge we have a service provider , the user who is seeking service pays the service provider for the service he/she is seeking. Before seeking service the user sends in some amount to the service provider and once the user is done with seeking service i.e, wishes to update the final state, the service provider updates the final state ON-CHAIN and takes in whatever amount that was required for providing this service.
The service provider verifies the signatures before finalising the state, and the signatures are also verified off chain before providing the service using elliptic curve recover operation. We pack the message, hash it and the digest is signed which is further verified on chain by splitSignature() based on solidity’s format where it’s checked if computed signer matches the actual signer.
If you want to read more into it!
https://blog.ricmoo.com/verifying-messages-in-solidity-50a94f82b2ca
You’ve reached the end , stay tuned for my next weeks blog :)
This week was the defi week and we got to work on building a decentralised exchange and also on layer-2 solutions like state channels.
https://speedrunethereum.com/builders/0xFEa64Acb621B94a6Bc9eAbc966427B0C3687B22A
To get an understanding how DEX really works I went through uniswap v1,v2 and v3. The newest v3 upgrade includes concentrated liquidity,Multiple fee tiers, and Customisable trading strategies.It also includes security features such as timelocks and access controls to prevent unauthorised access or manipulation of the contract.
Uniswap v3 has five fee tiers: 0.05%, 0.3%, 1%, 3%, and 5%.
The fee tier is determined by the distance between the two ticks that define the liquidity range of the position. Positions with a narrower tick range (closer ticks) earn a lower fee, while positions with a wider tick range (farther ticks) earn a higher fee.
// Allows a user to add liquidity to the pool by depositing an equal value of two tokens in a specific price range defined by `tickLower` and `tickUpper`
function mint(
address recipient,
int24 tickLower,
int24 tickUpper,
uint128 amount,
bytes calldata data
) external override lock returns (uint256 amount0, uint256 amount1) {
require(amount > 0);
(, int256 amount0Int, int256 amount1Int) = _modifyPosition(
ModifyPositionParams({
owner: recipient,
tickLower: tickLower,
tickUpper: tickUpper,
liquidityDelta: int256(amount).toInt128()
})
);
amount0 = uint256(amount0Int);
amount1 = uint256(amount1Int);
uint256 balance0Before;
uint256 balance1Before;
if (amount0 > 0) balance0Before = balance0();
if (amount1 > 0) balance1Before = balance1();
IUniswapV3MintCallback(msg.sender).uniswapV3MintCallback(amount0, amount1, data);
if (amount0 > 0) require(balance0Before.add(amount0) <= balance0(), 'M0');
if (amount1 > 0) require(balance1Before.add(amount1) <= balance1(), 'M1');
emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1);
}
In the DEX challenge we understood how AMM works, considering x * y = k
(amount of ETH in DEX ) * ( amount of tokens in DEX ) = k
The k only changes as liquidity is added.
How does AMM really work?
Consider An AMM has a pool of two assets, such as ETH and USDT.
Each asset is assigned a weight, which determines its relative value in the pool. For example, if ETH has a weight of 0.6 and USDT has a weight of 0.4, then ETH is considered to be worth more than USDT in the pool.
The product of the two assets' balances in the pool is always kept constant. So, if the pool starts with 1 ETH and 100 USDT, the product is 1*100=100.
When a trader wants to buy or sell one of the assets, they trade with the AMM instead of another person. The AMM uses the constant product formula to determine the new price of the asset being bought or sold.
For example, if a trader wants to buy 0.1 ETH with their USDT, the AMM will calculate the new balances of ETH and USDT that will keep the product constant. Let's say the new ETH balance is 1.1 and the new USDT balance is 90.91. The new price of ETH is then calculated by dividing the new USDT balance by the new ETH balance, which gives us 90.91/1.1=82.64 USDT per ETH.
The AMM charges a small fee for each trade, which is added to the liquidity pool. This incentivises liquidity providers to add funds to the pool and earn a share of the trading fees.
The functions for swapping from each asset to the other are ethToToken() and tokenToEth(). Each of these functions calculates the resulting amount of output asset using the price function that looks at the ratio of the reserves vs the input asset.
The deposit() function is used to deposit ETH and a corresponding ERC20 token into liquidity pool, which creates liquidity tokens in return. It calculates the amount of tokens to deposit based on the current reserve ratio of ETH and the ERC20 token in the pool, and then transfers the tokens from the sender to the liquidity pool contract. The function also mints new liquidity tokens for the sender and updates the total liquidity of the pool.
The withdraw() function is used to withdraw liquidity from the liquidity pool. The function takes as input the “amount of liquidity tokens” to withdraw and returns the corresponding “amount of ETH and ERC20 tokens”. The function calculates the amounts to withdraw based on the current reserve ratios and updates the sender's liquidity balance and the total liquidity of the pool accordingly. Finally, the function transfers the ETH and ERC20 tokens to the sender's address.
The function price() first includes 0.3% fee to the input token, and the 0.1% fee charged by the DEX on trades.
Watch this video for more understanding
function price(
uint256 xInput,
uint256 xReserves,
uint256 yReserves
) public view returns (uint256 yOutput) {
uint256 xInputWithFee = xInput.mul(997);
uint256 numerator = xInputWithFee.mul(yReserves);
uint256 denominator = (xReserves.mul(1000)).add(xInputWithFee);
return (numerator / denominator);
}
Challenges I faced during this challenge was to implement the AMM in code, and passing the testcases.
Before I go ahead with the state channels challenge, here’s an overview of what state channels are ? what is the need of state channels?
As said in this tweet Ethereum is not your grandpa’s database and you don’t store everything and anything onchain.
https://twitter.com/Kautukkundan/status/1626622768249962498?s=20
A number of approaches to scaling have been developed, collectively referred to as layer-2s (L2s). One of them being the State Channels.
In State Channels users agree on the initial state and multiple transactions are done off-chain and only the final state is updated on chain, in this way we save transaction fees and also the load on network.
They enable off-chain transactions between parties in a blockchain network. They involve opening a private communication channel between two parties and executing multiple transactions off-chain without the need to record them on the blockchain immediately.
In this challenge we have a service provider , the user who is seeking service pays the service provider for the service he/she is seeking. Before seeking service the user sends in some amount to the service provider and once the user is done with seeking service i.e, wishes to update the final state, the service provider updates the final state ON-CHAIN and takes in whatever amount that was required for providing this service.
The service provider verifies the signatures before finalising the state, and the signatures are also verified off chain before providing the service using elliptic curve recover operation. We pack the message, hash it and the digest is signed which is further verified on chain by splitSignature() based on solidity’s format where it’s checked if computed signer matches the actual signer.
If you want to read more into it!
https://blog.ricmoo.com/verifying-messages-in-solidity-50a94f82b2ca
You’ve reached the end , stay tuned for my next weeks blog :)
No comments yet