From 6bada988e2d5c2137bcb94816a44d1125d942fef Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 12 Apr 2024 13:59:53 -0300 Subject: [PATCH 01/48] feat(wip): adding support of ERC20 tokens. --- contracts/ethereum/src/PaymentRegistry.sol | 46 ++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/contracts/ethereum/src/PaymentRegistry.sol b/contracts/ethereum/src/PaymentRegistry.sol index e8eae662..da663dac 100644 --- a/contracts/ethereum/src/PaymentRegistry.sol +++ b/contracts/ethereum/src/PaymentRegistry.sol @@ -6,9 +6,13 @@ import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Own import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {IZkSync} from "@matterlabs/interfaces/IZkSync.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { + using SafeERC20 for IERC20; + event Transfer(uint256 indexed orderId, address srcAddress, address destAddress, uint256 amount, uint128 chainId); event ClaimPayment(uint256 indexed orderId, address destAddress, uint256 amount, uint128 chainId); event ClaimPaymentBatch(uint256[] orderIds, address[] destAddresses, uint256[] amounts, uint128 chainId); @@ -83,6 +87,48 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { emit Transfer(orderId, msg.sender, destAddress, msg.value, chainId); //2400 gas } + function registerMMerc20Allowance(address erc20) external onlyOwnerOrMM() { + IERC20(erc20).safeIncreaseAllowance(PaymentRegistry, type(uint256).max); + } + + function transferERC20(uint256 orderId, address destAddress, uint128 chainId, address erc20Address, uint256 amount) external onlyOwnerOrMM { + // Decide if MM fees are paid in ERC20 or in ETH. + // // If paid in ETH: + // // It is easy for MM to calculate how much fee is desirable for him to bridge the tokens + // // But An extra tx is needed containing this gas. + // // it is more expensive + + // // If paid in ERC20: + // // MM can get paid in a random coin, price may vary, more difficult for MM to determine how much ERC20 tokens are necesarry. + // // But MM only subscribes to desired ERC20. For example if he only bridges USDT, he may be willing to take USDT as fee for bridge. + // // No extra tx is needed to pay this gas fee, MM will simply transfer less ERC20 tokens than what he recieved. + // // It is cheaper + + + // Transfer process: + // analyzed alternative: + // transfer from MM to PaymentRegistry + // transfer from PaymentRegistry to User + // // This solution makes 2 ERC20 transfers, very expensive and unnecessary + // IERC20(erc20Address).safeTransferFrom(msg.sender, PaymentRegistry, amount); //this needs allowance + // IERC20(erc20Address).safeTransfer(destAddress, amount); //from this contract. should recieve the erc20 tokens first. Calling a TransferFrom(MM, to YAB) + + // Better alternative: + // directly transfer from MM to User + // // This solution seems appropriate. TransferFrom was built for exactly this. + + // before transferFrom I should check funds + allowance? or safeTransfer checks this? + // // Funds: + // // We shouldnt check for funds: MM shouldn't call Transfer if he doesnt have enough ERC20 tokens for the bridge. + + // // Allowance: + // // We have only 1 MM, we should allow PaymentRegistry to send all MM tokens. Set PaymentRegistry allowance of MM to unlimited. + // // This unlimited allowance should be set in a separate function. MM will allow to brdige x or y ERC20. + // // // we could even find new uses for this allowance. PaymentRegistry could be more intertwined with MM. Maybe automatically doing transfers in its name. + + IERC20(erc20Address).safeTransferFrom(msg.sender, destAddress, amount); //this needs allowance + } + function claimPaymentStarknet(uint256 orderId, address destAddress, uint256 amount) external payable onlyOwnerOrMM { _verifyTransferExistsStarknet(orderId, destAddress, amount); From 2e1d6d57fa6b40b104682fd8d5c30b2e65fe8f6f Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Mon, 15 Apr 2024 17:08:08 -0300 Subject: [PATCH 02/48] feat: function transferERC20() so MM can transfer an ERC20 --- contracts/ethereum/src/PaymentRegistry.sol | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/contracts/ethereum/src/PaymentRegistry.sol b/contracts/ethereum/src/PaymentRegistry.sol index da663dac..743d3327 100644 --- a/contracts/ethereum/src/PaymentRegistry.sol +++ b/contracts/ethereum/src/PaymentRegistry.sol @@ -14,6 +14,8 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { using SafeERC20 for IERC20; event Transfer(uint256 indexed orderId, address srcAddress, address destAddress, uint256 amount, uint128 chainId); + event TransferERC20(uint256 indexed orderId, address srcAddress, address destAddress, uint256 amount, uint128 chainId, address erc20Address); + event ClaimPayment(uint256 indexed orderId, address destAddress, uint256 amount, uint128 chainId); event ClaimPaymentBatch(uint256[] orderIds, address[] destAddresses, uint256[] amounts, uint128 chainId); @@ -96,6 +98,7 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { // // If paid in ETH: // // It is easy for MM to calculate how much fee is desirable for him to bridge the tokens // // But An extra tx is needed containing this gas. + // // // actually the same tx could contain the mm fee in --value // // it is more expensive // // If paid in ERC20: @@ -126,7 +129,15 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { // // This unlimited allowance should be set in a separate function. MM will allow to brdige x or y ERC20. // // // we could even find new uses for this allowance. PaymentRegistry could be more intertwined with MM. Maybe automatically doing transfers in its name. - IERC20(erc20Address).safeTransferFrom(msg.sender, destAddress, amount); //this needs allowance + require(amount > 0, "Invalid amount, should be higher than 0."); + + bytes32 index = keccak256(abi.encodePacked(orderId, destAddress, amount, chainId, erc20Address)); //added erc20Address + + require(transfers[index] == false, "Transfer already processed."); + transfers[index] = true; //now this transfer is in progress + + IERC20(erc20Address).safeTransferFrom(msg.sender, destAddress, amount); //this needs allowance, this reverts if failed + emit TransferERC20(orderId, msg.sender, destAddress, amount, chainId, erc20Address); } function claimPaymentStarknet(uint256 orderId, address destAddress, uint256 amount) external payable onlyOwnerOrMM { From cca48b9edc2a8577558eafd73834827991381139 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Mon, 15 Apr 2024 17:13:09 -0300 Subject: [PATCH 03/48] feat: claimPaymentZKSyncERC20 in PaymentRegistry --- contracts/ethereum/src/PaymentRegistry.sol | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/contracts/ethereum/src/PaymentRegistry.sol b/contracts/ethereum/src/PaymentRegistry.sol index 743d3327..c1b93c6b 100644 --- a/contracts/ethereum/src/PaymentRegistry.sol +++ b/contracts/ethereum/src/PaymentRegistry.sol @@ -17,6 +17,7 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { event TransferERC20(uint256 indexed orderId, address srcAddress, address destAddress, uint256 amount, uint128 chainId, address erc20Address); event ClaimPayment(uint256 indexed orderId, address destAddress, uint256 amount, uint128 chainId); + event ClaimPaymentERC20(uint256 indexed orderId, address destAddress, uint256 amount, uint128 chainId, address erc20Address); event ClaimPaymentBatch(uint256[] orderIds, address[] destAddresses, uint256[] amounts, uint128 chainId); event ModifiedZKSyncEscrowAddress(address newEscrowAddress); @@ -226,6 +227,34 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { emit ClaimPayment(orderId, destAddress, amount, ZKSyncChainId); //2100 gas } + function claimPaymentZKSyncERC20( + uint256 orderId, address destAddress, uint256 amount, + uint256 gasLimit, + uint256 gasPerPubdataByteLimit, address erc20Address + ) external payable onlyOwnerOrMM { + _verifyTransferExistsZKSyncERC20(orderId, destAddress, amount, erc20Address); + + bytes memory messageToL2 = abi.encodeWithSelector( + ZKSyncEscrowClaimPaymentSelector, + orderId, + destAddress, + amount, + erc20Address + ); + + _ZKSyncDiamondProxy.requestL2Transaction{value: msg.value}( + ZKSyncEscrowAddress, //L2 contract called + 0, //msg.value + messageToL2, //msg.calldata + gasLimit, + gasPerPubdataByteLimit, + new bytes[](0), //factory dependencies + msg.sender //refund recipient + ); + + emit ClaimPaymentERC20(orderId, destAddress, amount, ZKSyncChainId, erc20Address); + } + function claimPaymentBatchZKSync( uint256[] calldata orderIds, address[] calldata destAddresses, @@ -260,6 +289,11 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { emit ClaimPaymentBatch(orderIds, destAddresses, amounts, ZKSyncChainId); } + function _verifyTransferExistsZKSyncERC20(uint256 orderId, address destAddress, uint256 amount, address erc20Address) internal view { + bytes32 index = keccak256(abi.encodePacked(orderId, destAddress, amount, ZKSyncChainId, erc20Address)); + require(transfers[index] == true, "Transfer not found."); //if this is claimed twice, Escrow will know + } + function _verifyTransferExistsZKSync(uint256 orderId, address destAddress, uint256 amount) internal view { bytes32 index = keccak256(abi.encodePacked(orderId, destAddress, amount, ZKSyncChainId)); require(transfers[index] == true, "Transfer not found."); //if this is claimed twice, Escrow will know From 54f7ebbfaf2c803ef5171f75ba98891402dab4d8 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Mon, 15 Apr 2024 17:23:33 -0300 Subject: [PATCH 04/48] feat: claim_payment_erc20 in zksync --- contracts/zksync/contracts/escrow.sol | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/contracts/zksync/contracts/escrow.sol b/contracts/zksync/contracts/escrow.sol index 00e4a4c3..f1be88a8 100644 --- a/contracts/zksync/contracts/escrow.sol +++ b/contracts/zksync/contracts/escrow.sol @@ -7,12 +7,15 @@ import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; // import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; // import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; // TODO make upgradeable contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, UUPSUpgradeable { + using SafeERC20 for IERC20; + struct Order { address recipient_address; uint256 amount; @@ -22,6 +25,7 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, event SetOrder(uint256 order_id, address recipient_address, uint256 amount, uint256 fee); event ClaimPayment(uint256 order_id, address claimerAddress, uint256 amount); + event ClaimPaymentERC20(uint256 order_id, address claimerAddress, uint256 amount, address erc20Address); //storage @@ -92,6 +96,37 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, emit ClaimPayment(order_id, mm_zksync_wallet, amount); } + // l1 handler + function claim_payment_ERC20( + uint256 order_id, + address recipient_address, + uint256 amount, + address erc20Address + ) public whenNotPaused { + require(msg.sender == ethereum_payment_registry, 'Only PAYMENT_REGISTRY can call'); + require(_orders_pending[order_id], 'Order claimed or nonexistent'); + + Order memory current_order = _orders[order_id]; //TODO check if order is memory or calldata + require(current_order.recipient_address == recipient_address, 'recipient_address not match L1'); + require(current_order.amount == amount, 'amount not match L1'); + + _orders_pending[order_id] = false; + // uint256 payment_amount = current_order.amount + current_order.fee; // TODO check overflow + + + // Implement only 1 of these two: + //amount + fee in ETH: + IERC20(erc20Address).safeTransfer(mm_zksync_wallet, current_order.amount); + (bool success,) = payable(address(uint160(mm_zksync_wallet))).call{value: current_order.fee}(""); + // //amount + fee in ERC20: + // IERC20(erc20Address).safeTransfer(mm_zksync_wallet, current_order.amount + current_order.fee); + + + require(success, "Transfer failed."); + + emit ClaimPaymentERC20(order_id, mm_zksync_wallet, amount, erc20Address); + } + // l1 handler function claim_payment_batch( uint256[] calldata order_ids, From 1c3eb37b1b99b8143f4b6fb4c1349d4d03c42c10 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Mon, 15 Apr 2024 17:41:46 -0300 Subject: [PATCH 05/48] feat: set_order_erc20 in ZKSync escrow --- contracts/zksync/contracts/escrow.sol | 44 +++++++++++++++++++++------ 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/contracts/zksync/contracts/escrow.sol b/contracts/zksync/contracts/escrow.sol index f1be88a8..d501b80e 100644 --- a/contracts/zksync/contracts/escrow.sol +++ b/contracts/zksync/contracts/escrow.sol @@ -22,7 +22,15 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, uint256 fee; } + struct ERC20Order { + address recipient_address; + uint256 amount; + uint256 fee; + address erc20Address; + } + event SetOrder(uint256 order_id, address recipient_address, uint256 amount, uint256 fee); + event SetOrderERC20(uint256 order_id, address recipient_address, uint256 amount, uint256 fee, address erc20Address); event ClaimPayment(uint256 order_id, address claimerAddress, uint256 amount); event ClaimPaymentERC20(uint256 order_id, address claimerAddress, uint256 amount, address erc20Address); @@ -31,6 +39,7 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, //storage uint256 private _current_order_id; mapping(uint256 => Order) private _orders; + mapping(uint256 => ERC20Order) private _orders_erc20; mapping(uint256 => bool) private _orders_pending; mapping(uint256 => address) private _orders_senders; mapping(uint256 => uint256) private _orders_timestamps; @@ -74,6 +83,27 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, return _current_order_id-1; } + //Function recieves in msg.value the total fee for MM, and in amout the total tokens he wants to bridge + function set_order_erc20(address recipient_address, uint256 amount, address erc20Address) public payable whenNotPaused returns (uint256) { + require(msg.value > 0, 'some ETH must be sent as MM fees'); + require(amount > 0, 'some tokens must be sent to bridge'); + + //the following needs allowance, which is not set yet + require(IERC20(erc20Address).allowance(msg.sender, address(this)) >= amount, "Insuficient Allowance"); + IERC20(erc20Address).safeTransferFrom(msg.sender, address(this), amount); //will revert if failed + + ERC20Order memory new_order = ERC20Order({recipient_address: recipient_address, amount: amount, fee: msg.value, erc20Address: erc20Address}); + _orders_erc20[_current_order_id] = new_order; + _orders_pending[_current_order_id] = true; + _orders_senders[_current_order_id] = msg.sender; + _orders_timestamps[_current_order_id] = block.timestamp; + _current_order_id++; //this here to follow CEI pattern + + emit SetOrderERC20(_current_order_id-1, recipient_address, amount, msg.value, erc20Address); + + return _current_order_id-1; + } + // l1 handler function claim_payment( uint256 order_id, @@ -106,23 +136,17 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, require(msg.sender == ethereum_payment_registry, 'Only PAYMENT_REGISTRY can call'); require(_orders_pending[order_id], 'Order claimed or nonexistent'); - Order memory current_order = _orders[order_id]; //TODO check if order is memory or calldata + ERC20Order memory current_order = _orders_erc20[order_id]; //TODO check if order is memory or calldata require(current_order.recipient_address == recipient_address, 'recipient_address not match L1'); require(current_order.amount == amount, 'amount not match L1'); + require(current_order.erc20Address == erc20Address, 'erc20Address does not match L1'); _orders_pending[order_id] = false; - // uint256 payment_amount = current_order.amount + current_order.fee; // TODO check overflow - - // Implement only 1 of these two: //amount + fee in ETH: - IERC20(erc20Address).safeTransfer(mm_zksync_wallet, current_order.amount); + IERC20(erc20Address).safeTransfer(mm_zksync_wallet, current_order.amount); //will revert if failed (bool success,) = payable(address(uint160(mm_zksync_wallet))).call{value: current_order.fee}(""); - // //amount + fee in ERC20: - // IERC20(erc20Address).safeTransfer(mm_zksync_wallet, current_order.amount + current_order.fee); - - - require(success, "Transfer failed."); + require(success, "Fee transfer failed."); emit ClaimPaymentERC20(order_id, mm_zksync_wallet, amount, erc20Address); } From 075960db0a3791d7a71d54a0a7bdaafb20bbe561 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:46:37 -0300 Subject: [PATCH 06/48] feat: erc20 for L1 and L2 --- Makefile | 7 + contracts/ethereum/.env.example | 1 + contracts/ethereum/deploy_erc20.sh | 25 ++ .../contracts/token/ERC20/ERC20.sol | 312 ++++++++++++++++++ contracts/ethereum/script/Deploy_ERC20.s.sol | 18 + contracts/zksync/contracts/ERC20_L2.sol | 10 + contracts/zksync/deploy/deploy_erc20.ts | 16 + contracts/zksync/deploy_erc20.sh | 17 + contracts/zksync/package.json | 1 + 9 files changed, 407 insertions(+) create mode 100755 contracts/ethereum/deploy_erc20.sh create mode 100644 contracts/ethereum/lib/openzeppelin/contracts/token/ERC20/ERC20.sol create mode 100644 contracts/ethereum/script/Deploy_ERC20.s.sol create mode 100644 contracts/zksync/contracts/ERC20_L2.sol create mode 100644 contracts/zksync/deploy/deploy_erc20.ts create mode 100755 contracts/zksync/deploy_erc20.sh diff --git a/Makefile b/Makefile index 23038f0c..bb1999b3 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,9 @@ ethereum-upgrade: ethereum-build ethereum-set-escrow: @. ./contracts/ethereum/.env && . ./contracts/ethereum/set_starknet_escrow.sh +ethereum-deploy-erc20: ethereum-build + @. ./contracts/ethereum/.env && . ./contracts/ethereum/deploy_erc20.sh + ### STARKNET ### @@ -113,6 +116,10 @@ zksync-test-integration: . ./contracts/zksync/test/transfer.sh && \ . ./contracts/zksync/test/claim_payment.sh +zksync-deploy-erc20: + @make zksync-build && \ + . ./contracts/zksync/deploy_erc20.sh + # zksync-upgrade: WIP diff --git a/contracts/ethereum/.env.example b/contracts/ethereum/.env.example index fb4e644c..f6b8f549 100644 --- a/contracts/ethereum/.env.example +++ b/contracts/ethereum/.env.example @@ -12,6 +12,7 @@ STARKNET_CLAIM_PAYMENT_SELECTOR=<0x03636c566f6409560d55d5f6d1eb4ee163b096b4698c5 STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR=<0x0354a01e49fe07e43306a97ed84dbd5de8238c7d8ff616caa3444630cfc559e6> #hex value of starknet's claim_payment_batch selector ZKSYNC_CLAIM_PAYMENT_SELECTOR=<0xa5168739> #hex value of ZKSync's claim_payment selctor ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR=<0x156be1ae> #hex value of ZKSync's claim_payment_batch selctor +ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR=<0xb9738dd6> #hex value of ZKSync's claim_payment_erc20 selctor STARKNET_CHAIN_ID=<0x534e5f5345504f4c4941|0x534e5f4d41494e> #Sepolia | Mainnet ZKSYNC_CHAIN_ID=<300|324> # Sepolia | Mainnet diff --git a/contracts/ethereum/deploy_erc20.sh b/contracts/ethereum/deploy_erc20.sh new file mode 100755 index 00000000..a2add319 --- /dev/null +++ b/contracts/ethereum/deploy_erc20.sh @@ -0,0 +1,25 @@ +#!/bin/bash +. contracts/utils/colors.sh #for ANSI colors + +cd contracts/ethereum + +printf "${GREEN}\n=> [ETH] Deploying ERC20 ${COLOR_RESET}\n" + + +export ETHEREUM_PRIVATE_KEY=$ETHEREUM_PRIVATE_KEY + +RESULT_LOG=$(forge script ./script/Deploy_ERC20.s.sol --rpc-url $ETHEREUM_RPC --broadcast ${SKIP_VERIFY:---verify}) +# echo "$RESULT_LOG" #uncomment this line for debugging in detail + +# Getting result addresses +ERC20_ADDRESS=$(echo "$RESULT_LOG" | grep -Eo '0: address ([^\n]+)' | awk '{print $NF}') + +if [ -z "$ERC20_ADDRESS" ]; then + printf "\n${RED}ERROR:${COLOR_RESET}\n" + echo "ERC20_ADDRESS Variable is empty. Aborting execution.\n" + exit 1 +fi + +printf "${GREEN}\n=> [ETH] Deployed ERC20 address: $ERC20_ADDRESS ${COLOR_RESET}\n" + +cd ../.. #to reset working directory diff --git a/contracts/ethereum/lib/openzeppelin/contracts/token/ERC20/ERC20.sol b/contracts/ethereum/lib/openzeppelin/contracts/token/ERC20/ERC20.sol new file mode 100644 index 00000000..a7598a66 --- /dev/null +++ b/contracts/ethereum/lib/openzeppelin/contracts/token/ERC20/ERC20.sol @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol) + +pragma solidity ^0.8.20; + +import {IERC20} from "./IERC20.sol"; +import {IERC20Metadata} from "./extensions/IERC20Metadata.sol"; +import {Context} from "../../utils/Context.sol"; +import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol"; + +/** + * @dev Implementation of the {IERC20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * + * TIP: For a detailed writeup see our guide + * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How + * to implement supply mechanisms]. + * + * The default value of {decimals} is 18. To change this, you should override + * this function so it returns a different value. + * + * We have followed general OpenZeppelin Contracts guidelines: functions revert + * instead returning `false` on failure. This behavior is nonetheless + * conventional and does not conflict with the expectations of ERC-20 + * applications. + */ +abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { + mapping(address account => uint256) private _balances; + + mapping(address account => mapping(address spender => uint256)) private _allowances; + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + + /** + * @dev Sets the values for {name} and {symbol}. + * + * All two of these values are immutable: they can only be set once during + * construction. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view virtual returns (string memory) { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view virtual returns (string memory) { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5.05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the default value returned by this function, unless + * it's overridden. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view virtual returns (uint8) { + return 18; + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view virtual returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view virtual returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - the caller must have a balance of at least `value`. + */ + function transfer(address to, uint256 value) public virtual returns (bool) { + address owner = _msgSender(); + _transfer(owner, to, value); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on + * `transferFrom`. This is semantically equivalent to an infinite approval. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 value) public virtual returns (bool) { + address owner = _msgSender(); + _approve(owner, spender, value); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Skips emitting an {Approval} event indicating an allowance update. This is not + * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve]. + * + * NOTE: Does not update the allowance if the current allowance + * is the maximum `uint256`. + * + * Requirements: + * + * - `from` and `to` cannot be the zero address. + * - `from` must have a balance of at least `value`. + * - the caller must have allowance for ``from``'s tokens of at least + * `value`. + */ + function transferFrom(address from, address to, uint256 value) public virtual returns (bool) { + address spender = _msgSender(); + _spendAllowance(from, spender, value); + _transfer(from, to, value); + return true; + } + + /** + * @dev Moves a `value` amount of tokens from `from` to `to`. + * + * This internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * NOTE: This function is not virtual, {_update} should be overridden instead. + */ + function _transfer(address from, address to, uint256 value) internal { + if (from == address(0)) { + revert ERC20InvalidSender(address(0)); + } + if (to == address(0)) { + revert ERC20InvalidReceiver(address(0)); + } + _update(from, to, value); + } + + /** + * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` + * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding + * this function. + * + * Emits a {Transfer} event. + */ + function _update(address from, address to, uint256 value) internal virtual { + if (from == address(0)) { + // Overflow check required: The rest of the code assumes that totalSupply never overflows + _totalSupply += value; + } else { + uint256 fromBalance = _balances[from]; + if (fromBalance < value) { + revert ERC20InsufficientBalance(from, fromBalance, value); + } + unchecked { + // Overflow not possible: value <= fromBalance <= totalSupply. + _balances[from] = fromBalance - value; + } + } + + if (to == address(0)) { + unchecked { + // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply. + _totalSupply -= value; + } + } else { + unchecked { + // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256. + _balances[to] += value; + } + } + + emit Transfer(from, to, value); + } + + /** + * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0). + * Relies on the `_update` mechanism + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * NOTE: This function is not virtual, {_update} should be overridden instead. + */ + function _mint(address account, uint256 value) internal { + if (account == address(0)) { + revert ERC20InvalidReceiver(address(0)); + } + _update(address(0), account, value); + } + + /** + * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply. + * Relies on the `_update` mechanism. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * NOTE: This function is not virtual, {_update} should be overridden instead + */ + function _burn(address account, uint256 value) internal { + if (account == address(0)) { + revert ERC20InvalidSender(address(0)); + } + _update(account, address(0), value); + } + + /** + * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens. + * + * This internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + * + * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. + */ + function _approve(address owner, address spender, uint256 value) internal { + _approve(owner, spender, value, true); + } + + /** + * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event. + * + * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by + * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any + * `Approval` event during `transferFrom` operations. + * + * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to + * true using the following override: + * + * ```solidity + * function _approve(address owner, address spender, uint256 value, bool) internal virtual override { + * super._approve(owner, spender, value, true); + * } + * ``` + * + * Requirements are the same as {_approve}. + */ + function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { + if (owner == address(0)) { + revert ERC20InvalidApprover(address(0)); + } + if (spender == address(0)) { + revert ERC20InvalidSpender(address(0)); + } + _allowances[owner][spender] = value; + if (emitEvent) { + emit Approval(owner, spender, value); + } + } + + /** + * @dev Updates `owner` s allowance for `spender` based on spent `value`. + * + * Does not update the allowance value in case of infinite allowance. + * Revert if not enough allowance is available. + * + * Does not emit an {Approval} event. + */ + function _spendAllowance(address owner, address spender, uint256 value) internal virtual { + uint256 currentAllowance = allowance(owner, spender); + if (currentAllowance != type(uint256).max) { + if (currentAllowance < value) { + revert ERC20InsufficientAllowance(spender, currentAllowance, value); + } + unchecked { + _approve(owner, spender, currentAllowance - value, false); + } + } + } +} \ No newline at end of file diff --git a/contracts/ethereum/script/Deploy_ERC20.s.sol b/contracts/ethereum/script/Deploy_ERC20.s.sol new file mode 100644 index 00000000..4eb69ccf --- /dev/null +++ b/contracts/ethereum/script/Deploy_ERC20.s.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +import {Script} from "forge-std/Script.sol"; +import {UriCoin} from "../src/ERC20_L1.sol"; + +contract Deploy is Script { + function run() external returns (address) { + uint256 deployerPrivateKey = vm.envUint("ETHEREUM_PRIVATE_KEY"); + vm.startBroadcast(deployerPrivateKey); + + UriCoin uriCoin = new UriCoin(); + + vm.stopBroadcast(); + + return (address(uriCoin)); + } +} diff --git a/contracts/zksync/contracts/ERC20_L2.sol b/contracts/zksync/contracts/ERC20_L2.sol new file mode 100644 index 00000000..22fa6629 --- /dev/null +++ b/contracts/zksync/contracts/ERC20_L2.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract UriCoin is ERC20 { + constructor() ERC20("UriCoin", "Uri") { + _mint(0xB321099cf86D9BB913b891441B014c03a6CcFc54, 1000000); //hardcoded recipient + initial_supply + } +} \ No newline at end of file diff --git a/contracts/zksync/deploy/deploy_erc20.ts b/contracts/zksync/deploy/deploy_erc20.ts new file mode 100644 index 00000000..1d17ef92 --- /dev/null +++ b/contracts/zksync/deploy/deploy_erc20.ts @@ -0,0 +1,16 @@ +import { deployContractWithProxy } from "./utils"; +import { deployContract } from "./utils"; +// import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import * as dotenv from 'dotenv'; +import { utils } from "zksync-ethers"; + + +// It will deploy a Escrow contract to selected network +export default async function () { + const escrowArtifactName = "UriCoin"; + + const escrowConstructorArguments = []; + const escrow = await deployContract(escrowArtifactName, escrowConstructorArguments); + + console.log("Deploy erc20 result:", escrow); +} diff --git a/contracts/zksync/deploy_erc20.sh b/contracts/zksync/deploy_erc20.sh new file mode 100755 index 00000000..4e317c3b --- /dev/null +++ b/contracts/zksync/deploy_erc20.sh @@ -0,0 +1,17 @@ +#!/bin/bash +. contracts/utils/colors.sh #for ANSI colors + +cd ./contracts/zksync/ + +ERC20_CONTRACT_ADDRESS=$(yarn deploy-erc20 | grep "Contract address:" | egrep -i -o '0x[a-zA-Z0-9]{40}') + +if [ -z "$ERC20_CONTRACT_ADDRESS" ]; then + printf "\n${RED}ERROR:${COLOR_RESET}\n" + echo "ERC20_CONTRACT_ADDRESS Variable is empty. Aborting execution.\n" + exit 1 +fi + +printf "${CYAN}[ZKSync] ERC20 Address: $ERC20_CONTRACT_ADDRESS${COLOR_RESET}\n" + +cd ../.. + diff --git a/contracts/zksync/package.json b/contracts/zksync/package.json index 5648a349..cea88e54 100644 --- a/contracts/zksync/package.json +++ b/contracts/zksync/package.json @@ -3,6 +3,7 @@ "private": true, "packageManager": "yarn@1.22.21", "scripts": { + "deploy-erc20": "hardhat deploy-zksync --network zkSyncSepoliaTestnet --script deploy_erc20.ts", "deploy": "hardhat deploy-zksync --network zkSyncSepoliaTestnet --script deploy.ts", "deploy-devnet": "hardhat deploy-zksync --network dockerizedNode --script deploy.ts", "compile": "hardhat compile", From ec2f5295b2349411047be71f58a3b71552cedddc Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:47:18 -0300 Subject: [PATCH 07/48] feat: L1 erc20 compatibility --- contracts/ethereum/.env.test | 2 + contracts/ethereum/script/Deploy.s.sol | 2 + contracts/ethereum/src/PaymentRegistry.sol | 117 ++++++++++++--------- 3 files changed, 70 insertions(+), 51 deletions(-) diff --git a/contracts/ethereum/.env.test b/contracts/ethereum/.env.test index 42e5dd41..139b59dd 100644 --- a/contracts/ethereum/.env.test +++ b/contracts/ethereum/.env.test @@ -8,5 +8,7 @@ STARKNET_CLAIM_PAYMENT_SELECTOR=0x03636c566f6409560d55d5f6d1eb4ee163b096b4698c50 STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR=0x0354a01e49fe07e43306a97ed84dbd5de8238c7d8ff616caa3444630cfc559e6 ZKSYNC_CLAIM_PAYMENT_SELECTOR=0xa5168739 ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR=0x156be1ae +ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR=0xb9738dd6 + STARKNET_CHAIN_ID=0x534e5f5345504f4c4941 ZKSYNC_CHAIN_ID=300 diff --git a/contracts/ethereum/script/Deploy.s.sol b/contracts/ethereum/script/Deploy.s.sol index 4aa4958e..06c071c9 100644 --- a/contracts/ethereum/script/Deploy.s.sol +++ b/contracts/ethereum/script/Deploy.s.sol @@ -17,6 +17,7 @@ contract Deploy is Script { address ZKSYNC_DIAMOND_PROXY_ADDRESS = vm.envAddress("ZKSYNC_DIAMOND_PROXY_ADDRESS"); bytes4 ZKSYNC_CLAIM_PAYMENT_SELECTOR = bytes4(vm.envBytes("ZKSYNC_CLAIM_PAYMENT_SELECTOR")); bytes4 ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR = bytes4(vm.envBytes("ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR")); + bytes4 ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR = bytes4(vm.envBytes("ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR")); uint128 STARKNET_CHAIN_ID = uint128(vm.envUint("STARKNET_CHAIN_ID")); uint128 ZKSYNC_CHAIN_ID = uint128(vm.envUint("ZKSYNC_CHAIN_ID")); @@ -31,6 +32,7 @@ contract Deploy is Script { ZKSYNC_DIAMOND_PROXY_ADDRESS, ZKSYNC_CLAIM_PAYMENT_SELECTOR, ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR, + ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR, STARKNET_CHAIN_ID, ZKSYNC_CHAIN_ID ); diff --git a/contracts/ethereum/src/PaymentRegistry.sol b/contracts/ethereum/src/PaymentRegistry.sol index c1b93c6b..1f501c88 100644 --- a/contracts/ethereum/src/PaymentRegistry.sol +++ b/contracts/ethereum/src/PaymentRegistry.sol @@ -7,7 +7,7 @@ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Ini import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {IZkSync} from "@matterlabs/interfaces/IZkSync.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; - + contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { @@ -26,6 +26,7 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { event ModifiedStarknetClaimPaymentBatchSelector(uint256 newEscrowClaimPaymentBatchSelector); event ModifiedZKSyncClaimPaymentSelector(bytes4 newZKSyncEscrowClaimPaymentSelector); event ModifiedZKSyncClaimPaymentBatchSelector(bytes4 newZKSyncEscrowClaimPaymentBatchSelector); + event ModifiedZKSyncClaimPaymentERC20Selector(bytes4 newZKSyncEscrowClaimPaymentERC20Selector); mapping(bytes32 => bool) public transfers; address public marketMaker; @@ -35,6 +36,7 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { uint256 public StarknetEscrowClaimPaymentBatchSelector; bytes4 public ZKSyncEscrowClaimPaymentSelector; bytes4 public ZKSyncEscrowClaimPaymentBatchSelector; + bytes4 public ZKSyncEscrowClaimPaymentERC20Selector; IZkSync private _ZKSyncDiamondProxy; IStarknetMessaging private _snMessaging; @@ -56,6 +58,7 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { address ZKSyncDiamondProxyAddress, bytes4 ZKSyncEscrowClaimPaymentSelector_, bytes4 ZKSyncEscrowClaimPaymentBatchSelector_, + bytes4 ZKSyncEscrowClaimPaymentERC20Selector_, uint128 StarknetChainId_, uint128 ZKSyncChainId_) public initializer { __Ownable_init(msg.sender); @@ -68,6 +71,7 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { StarknetEscrowClaimPaymentBatchSelector = StarknetEscrowClaimPaymentBatchSelector_; ZKSyncEscrowClaimPaymentSelector = ZKSyncEscrowClaimPaymentSelector_; ZKSyncEscrowClaimPaymentBatchSelector = ZKSyncEscrowClaimPaymentBatchSelector_; + ZKSyncEscrowClaimPaymentERC20Selector = ZKSyncEscrowClaimPaymentERC20Selector_; StarknetChainId = StarknetChainId_; ZKSyncChainId = ZKSyncChainId_; @@ -75,26 +79,11 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { marketMaker = marketMaker_; } - //TODO: change orderID to uint32 - function transfer(uint256 orderId, address destAddress, uint128 chainId) external payable onlyOwnerOrMM { - require(msg.value > 0, "Invalid amount, should be higher than 0."); - - bytes32 index = keccak256(abi.encodePacked(orderId, destAddress, msg.value, chainId)); - - require(transfers[index] == false, "Transfer already processed."); - transfers[index] = true; //now this transfer is in progress - - (bool success,) = payable(destAddress).call{value: msg.value}(""); //34000 gas - - require(success, "Transfer failed."); - emit Transfer(orderId, msg.sender, destAddress, msg.value, chainId); //2400 gas - } - function registerMMerc20Allowance(address erc20) external onlyOwnerOrMM() { - IERC20(erc20).safeIncreaseAllowance(PaymentRegistry, type(uint256).max); + IERC20(erc20).safeIncreaseAllowance(address(this), type(uint256).max); } - function transferERC20(uint256 orderId, address destAddress, uint128 chainId, address erc20Address, uint256 amount) external onlyOwnerOrMM { + function transferERC20(uint256 orderId, address destAddress, uint128 chainId, address l1_erc20_address, uint256 amount) external onlyOwnerOrMM { // Decide if MM fees are paid in ERC20 or in ETH. // // If paid in ETH: // // It is easy for MM to calculate how much fee is desirable for him to bridge the tokens @@ -131,14 +120,62 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { // // // we could even find new uses for this allowance. PaymentRegistry could be more intertwined with MM. Maybe automatically doing transfers in its name. require(amount > 0, "Invalid amount, should be higher than 0."); + require(IERC20(l1_erc20_address).balanceOf(msg.sender) >= amount, "MM has insufficient balance"); + require(IERC20(l1_erc20_address).allowance(msg.sender, address(this)) >= amount, "PaymentRegistry has insufficient allowance"); - bytes32 index = keccak256(abi.encodePacked(orderId, destAddress, amount, chainId, erc20Address)); //added erc20Address + bytes32 index = keccak256(abi.encodePacked(orderId, destAddress, amount, chainId, l1_erc20_address)); //added erc20Address require(transfers[index] == false, "Transfer already processed."); transfers[index] = true; //now this transfer is in progress - IERC20(erc20Address).safeTransferFrom(msg.sender, destAddress, amount); //this needs allowance, this reverts if failed - emit TransferERC20(orderId, msg.sender, destAddress, amount, chainId, erc20Address); + IERC20(l1_erc20_address).safeTransferFrom(msg.sender, destAddress, amount); //this reverts if failed + emit TransferERC20(orderId, msg.sender, destAddress, amount, chainId, l1_erc20_address); + } + + function claimPaymentZKSyncERC20( + uint256 orderId, + address destAddress, + uint256 amount, + uint256 gasLimit, + uint256 gasPerPubdataByteLimit, + address l1_erc20_address + ) external payable onlyOwnerOrMM { + _verifyTransferExistsZKSyncERC20(orderId, destAddress, amount, l1_erc20_address); + + bytes memory messageToL2 = abi.encodeWithSelector( + ZKSyncEscrowClaimPaymentERC20Selector, + orderId, + destAddress, + amount, + l1_erc20_address + ); + + _ZKSyncDiamondProxy.requestL2Transaction{value: msg.value}( + ZKSyncEscrowAddress, //L2 contract called + 0, //msg.value + messageToL2, //msg.calldata + gasLimit, + gasPerPubdataByteLimit, + new bytes[](0), //factory dependencies + msg.sender //refund recipient + ); + + emit ClaimPaymentERC20(orderId, destAddress, amount, ZKSyncChainId, l1_erc20_address); + } + + //TODO: change orderID to uint32 + function transfer(uint256 orderId, address destAddress, uint128 chainId) external payable onlyOwnerOrMM { + require(msg.value > 0, "Invalid amount, should be higher than 0."); + + bytes32 index = keccak256(abi.encodePacked(orderId, destAddress, msg.value, chainId)); + + require(transfers[index] == false, "Transfer already processed."); + transfers[index] = true; //now this transfer is in progress + + (bool success,) = payable(destAddress).call{value: msg.value}(""); //34000 gas + + require(success, "Transfer failed."); + emit Transfer(orderId, msg.sender, destAddress, msg.value, chainId); //2400 gas } function claimPaymentStarknet(uint256 orderId, address destAddress, uint256 amount) external payable onlyOwnerOrMM { @@ -227,34 +264,6 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { emit ClaimPayment(orderId, destAddress, amount, ZKSyncChainId); //2100 gas } - function claimPaymentZKSyncERC20( - uint256 orderId, address destAddress, uint256 amount, - uint256 gasLimit, - uint256 gasPerPubdataByteLimit, address erc20Address - ) external payable onlyOwnerOrMM { - _verifyTransferExistsZKSyncERC20(orderId, destAddress, amount, erc20Address); - - bytes memory messageToL2 = abi.encodeWithSelector( - ZKSyncEscrowClaimPaymentSelector, - orderId, - destAddress, - amount, - erc20Address - ); - - _ZKSyncDiamondProxy.requestL2Transaction{value: msg.value}( - ZKSyncEscrowAddress, //L2 contract called - 0, //msg.value - messageToL2, //msg.calldata - gasLimit, - gasPerPubdataByteLimit, - new bytes[](0), //factory dependencies - msg.sender //refund recipient - ); - - emit ClaimPaymentERC20(orderId, destAddress, amount, ZKSyncChainId, erc20Address); - } - function claimPaymentBatchZKSync( uint256[] calldata orderIds, address[] calldata destAddresses, @@ -289,8 +298,8 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { emit ClaimPaymentBatch(orderIds, destAddresses, amounts, ZKSyncChainId); } - function _verifyTransferExistsZKSyncERC20(uint256 orderId, address destAddress, uint256 amount, address erc20Address) internal view { - bytes32 index = keccak256(abi.encodePacked(orderId, destAddress, amount, ZKSyncChainId, erc20Address)); + function _verifyTransferExistsZKSyncERC20(uint256 orderId, address destAddress, uint256 amount, address l1_erc20_address) internal view { + bytes32 index = keccak256(abi.encodePacked(orderId, destAddress, amount, ZKSyncChainId, l1_erc20_address)); require(transfers[index] == true, "Transfer not found."); //if this is claimed twice, Escrow will know } @@ -329,6 +338,12 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { emit ModifiedZKSyncClaimPaymentBatchSelector(ZKSyncEscrowClaimPaymentBatchSelector); } + function setZKSyncEscrowClaimPaymentERC20Selector(bytes4 NewZKSyncEscrowClaimPaymentERC20Selector) external onlyOwner { + ZKSyncEscrowClaimPaymentERC20Selector = NewZKSyncEscrowClaimPaymentERC20Selector; + emit ModifiedZKSyncClaimPaymentERC20Selector(ZKSyncEscrowClaimPaymentERC20Selector); + + } + //// MM ACL: From 85ec54a0e35755650a2ccddc656b693737c195b9 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:47:42 -0300 Subject: [PATCH 08/48] test: add new init values to tests --- contracts/ethereum/src/ERC20_L1.sol | 10 ++++++++++ contracts/ethereum/test/ACL.t.sol | 3 ++- contracts/ethereum/test/Transfer_Claim_SN.t.sol | 3 ++- contracts/ethereum/test/Transfer_Claim_ZKSync.t.sol | 3 ++- 4 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 contracts/ethereum/src/ERC20_L1.sol diff --git a/contracts/ethereum/src/ERC20_L1.sol b/contracts/ethereum/src/ERC20_L1.sol new file mode 100644 index 00000000..532eb593 --- /dev/null +++ b/contracts/ethereum/src/ERC20_L1.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; //using upgradeable bc it is the one I have downloaded in my OZ lib + +contract UriCoin is ERC20 { + constructor() ERC20("UriCoin", "Uri") { + _mint(0xda963fA72caC2A3aC01c642062fba3C099993D56, 1000000); //hardcoded recipient + initial_supply + } +} diff --git a/contracts/ethereum/test/ACL.t.sol b/contracts/ethereum/test/ACL.t.sol index 62d4e665..adfb51ac 100644 --- a/contracts/ethereum/test/ACL.t.sol +++ b/contracts/ethereum/test/ACL.t.sol @@ -19,6 +19,7 @@ contract TransferTest is Test { address ZKSYNC_DIAMOND_PROXY_ADDRESS = 0x2eD8eF54a16bBF721a318bd5a5C0F39Be70eaa65; bytes4 ZKSYNC_CLAIM_PAYMENT_SELECTOR = 0xa5168739; bytes4 ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR = 0x156be1ae; + bytes4 ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR = 0xb9738dd6; uint128 STARKNET_CHAIN_ID = 0x534e5f5345504f4c4941; uint128 ZKSYNC_CHAIN_ID = 300; @@ -29,7 +30,7 @@ contract TransferTest is Test { yab = new PaymentRegistry(); proxy = new ERC1967Proxy(address(yab), ""); yab_caller = PaymentRegistry(address(proxy)); - yab_caller.initialize(STARKNET_MESSAGING_ADDRESS, STARKNET_CLAIM_PAYMENT_SELECTOR, STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR, MM_ETHEREUM_WALLET_ADDRESS, ZKSYNC_DIAMOND_PROXY_ADDRESS, ZKSYNC_CLAIM_PAYMENT_SELECTOR, ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR, STARKNET_CHAIN_ID, ZKSYNC_CHAIN_ID); + yab_caller.initialize(STARKNET_MESSAGING_ADDRESS, STARKNET_CLAIM_PAYMENT_SELECTOR, STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR, MM_ETHEREUM_WALLET_ADDRESS, ZKSYNC_DIAMOND_PROXY_ADDRESS, ZKSYNC_CLAIM_PAYMENT_SELECTOR, ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR, ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR, STARKNET_CHAIN_ID, ZKSYNC_CHAIN_ID); vm.stopPrank(); } diff --git a/contracts/ethereum/test/Transfer_Claim_SN.t.sol b/contracts/ethereum/test/Transfer_Claim_SN.t.sol index 20bab404..297b7f95 100644 --- a/contracts/ethereum/test/Transfer_Claim_SN.t.sol +++ b/contracts/ethereum/test/Transfer_Claim_SN.t.sol @@ -21,6 +21,7 @@ contract TransferTest is Test { address ZKSYNC_DIAMOND_PROXY_ADDRESS = 0x2eD8eF54a16bBF721a318bd5a5C0F39Be70eaa65; bytes4 ZKSYNC_CLAIM_PAYMENT_SELECTOR = 0xa5168739; bytes4 ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR = 0x156be1ae; + bytes4 ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR = 0xb9738dd6; uint128 STARKNET_CHAIN_ID = 0x534e5f5345504f4c4941; uint128 ZKSYNC_CHAIN_ID = 300; @@ -31,7 +32,7 @@ contract TransferTest is Test { yab = new PaymentRegistry(); proxy = new ERC1967Proxy(address(yab), ""); yab_caller = PaymentRegistry(address(proxy)); - yab_caller.initialize(STARKNET_MESSAGING_ADDRESS, STARKNET_CLAIM_PAYMENT_SELECTOR, STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR, MM_ETHEREUM_WALLET_ADDRESS, ZKSYNC_DIAMOND_PROXY_ADDRESS, ZKSYNC_CLAIM_PAYMENT_SELECTOR, ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR, STARKNET_CHAIN_ID, ZKSYNC_CHAIN_ID); + yab_caller.initialize(STARKNET_MESSAGING_ADDRESS, STARKNET_CLAIM_PAYMENT_SELECTOR, STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR, MM_ETHEREUM_WALLET_ADDRESS, ZKSYNC_DIAMOND_PROXY_ADDRESS, ZKSYNC_CLAIM_PAYMENT_SELECTOR, ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR, ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR, STARKNET_CHAIN_ID, ZKSYNC_CHAIN_ID); // Mock calls to Starknet Messaging contract vm.mockCall( STARKNET_MESSAGING_ADDRESS, diff --git a/contracts/ethereum/test/Transfer_Claim_ZKSync.t.sol b/contracts/ethereum/test/Transfer_Claim_ZKSync.t.sol index 0d412eb5..d393e978 100644 --- a/contracts/ethereum/test/Transfer_Claim_ZKSync.t.sol +++ b/contracts/ethereum/test/Transfer_Claim_ZKSync.t.sol @@ -20,6 +20,7 @@ contract TransferTest is Test { address ZKSYNC_DIAMOND_PROXY_ADDRESS = 0x2eD8eF54a16bBF721a318bd5a5C0F39Be70eaa65; bytes4 ZKSYNC_CLAIM_PAYMENT_SELECTOR = 0xa5168739; bytes4 ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR = 0x156be1ae; + bytes4 ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR = 0xb9738dd6; uint128 STARKNET_CHAIN_ID = 0x534e5f5345504f4c4941; uint128 ZKSYNC_CHAIN_ID = 300; @@ -30,7 +31,7 @@ contract TransferTest is Test { yab = new PaymentRegistry(); proxy = new ERC1967Proxy(address(yab), ""); yab_caller = PaymentRegistry(address(proxy)); - yab_caller.initialize(STARKNET_MESSAGING_ADDRESS, STARKNET_CLAIM_PAYMENT_SELECTOR, STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR, MM_ETHEREUM_WALLET_ADDRESS, ZKSYNC_DIAMOND_PROXY_ADDRESS, ZKSYNC_CLAIM_PAYMENT_SELECTOR, ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR, STARKNET_CHAIN_ID, ZKSYNC_CHAIN_ID); + yab_caller.initialize(STARKNET_MESSAGING_ADDRESS, STARKNET_CLAIM_PAYMENT_SELECTOR, STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR, MM_ETHEREUM_WALLET_ADDRESS, ZKSYNC_DIAMOND_PROXY_ADDRESS, ZKSYNC_CLAIM_PAYMENT_SELECTOR, ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR, ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR, STARKNET_CHAIN_ID, ZKSYNC_CHAIN_ID); //Mock calls to ZKSync Mailbox contract vm.mockCall( ZKSYNC_DIAMOND_PROXY_ADDRESS, From 90179d5356b7bd1a6d38bfb513a40bed7edd6f7f Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:47:54 -0300 Subject: [PATCH 09/48] feat: ZKSync escrow erc20 compatibility --- contracts/zksync/contracts/escrow.sol | 104 ++++++++++++++------------ 1 file changed, 56 insertions(+), 48 deletions(-) diff --git a/contracts/zksync/contracts/escrow.sol b/contracts/zksync/contracts/escrow.sol index d501b80e..0c2b5899 100644 --- a/contracts/zksync/contracts/escrow.sol +++ b/contracts/zksync/contracts/escrow.sol @@ -26,14 +26,15 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, address recipient_address; uint256 amount; uint256 fee; - address erc20Address; + address l1_erc20_address; + address l2_erc20_address; } event SetOrder(uint256 order_id, address recipient_address, uint256 amount, uint256 fee); - event SetOrderERC20(uint256 order_id, address recipient_address, uint256 amount, uint256 fee, address erc20Address); + event SetOrderERC20(uint256 order_id, address recipient_address, uint256 amount, uint256 fee, address l1_erc20_address, address l2_erc20_address); - event ClaimPayment(uint256 order_id, address claimerAddress, uint256 amount); - event ClaimPaymentERC20(uint256 order_id, address claimerAddress, uint256 amount, address erc20Address); + event ClaimPayment(uint256 order_id, address claimer_address, uint256 amount); + event ClaimPaymentERC20(uint256 order_id, address claimer_address, uint256 amount, address l1_erc20_address); //storage @@ -60,95 +61,102 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, // FUNCTIONS : - function get_order(uint256 order_id) public view returns (Order memory) { - return _orders[order_id]; - } - - //Function recieves in msg.value the total value, and in fee the user specifies what portion of that msg.value is fee for MM - function set_order(address recipient_address, uint256 fee) public payable whenNotPaused returns (uint256) { - require(msg.value > 0, 'some ETH must be sent'); - require(msg.value > fee, 'ETH sent must be more than fee'); - - uint256 bridge_amount = msg.value - fee; //no underflow since previous check is made - - Order memory new_order = Order({recipient_address: recipient_address, amount: bridge_amount, fee: fee}); - _orders[_current_order_id] = new_order; - _orders_pending[_current_order_id] = true; - _orders_senders[_current_order_id] = msg.sender; - _orders_timestamps[_current_order_id] = block.timestamp; - _current_order_id++; //this here to follow CEI pattern - - emit SetOrder(_current_order_id-1, recipient_address, bridge_amount, fee); - - return _current_order_id-1; + function increase_erc20_allowance(address l2_erc20_address, uint256 amount) public whenNotPaused { + //this is wrongggggg, i think there is no way of increasing allowance like this + // F + IERC20(l2_erc20_address).safeIncreaseAllowance(msg.sender, amount); //do allow max amount? same price to execute and saves a posterior need of execution } //Function recieves in msg.value the total fee for MM, and in amout the total tokens he wants to bridge - function set_order_erc20(address recipient_address, uint256 amount, address erc20Address) public payable whenNotPaused returns (uint256) { + function set_order_erc20(address recipient_address, uint256 amount, address l1_erc20_address, address l2_erc20_address) public payable whenNotPaused returns (uint256) { require(msg.value > 0, 'some ETH must be sent as MM fees'); require(amount > 0, 'some tokens must be sent to bridge'); - //the following needs allowance, which is not set yet - require(IERC20(erc20Address).allowance(msg.sender, address(this)) >= amount, "Insuficient Allowance"); - IERC20(erc20Address).safeTransferFrom(msg.sender, address(this), amount); //will revert if failed + //the following needs allowance, which is not set automatically + require(IERC20(l2_erc20_address).balanceOf(msg.sender) >= amount, "User has insuficient funds"); + require(IERC20(l2_erc20_address).allowance(msg.sender, address(this)) >= amount, "Escrow has insuficient allowance"); + IERC20(l2_erc20_address).safeTransferFrom(msg.sender, address(this), amount); //will revert if failed - ERC20Order memory new_order = ERC20Order({recipient_address: recipient_address, amount: amount, fee: msg.value, erc20Address: erc20Address}); + ERC20Order memory new_order = ERC20Order({recipient_address: recipient_address, amount: amount, fee: msg.value, l1_erc20_address: l1_erc20_address, l2_erc20_address: l2_erc20_address}); _orders_erc20[_current_order_id] = new_order; _orders_pending[_current_order_id] = true; _orders_senders[_current_order_id] = msg.sender; _orders_timestamps[_current_order_id] = block.timestamp; _current_order_id++; //this here to follow CEI pattern - emit SetOrderERC20(_current_order_id-1, recipient_address, amount, msg.value, erc20Address); + emit SetOrderERC20(_current_order_id-1, recipient_address, amount, msg.value, l1_erc20_address, l2_erc20_address); return _current_order_id-1; } // l1 handler - function claim_payment( + function claim_payment_erc20( uint256 order_id, address recipient_address, - uint256 amount + uint256 amount, + address l1_erc20_address ) public whenNotPaused { require(msg.sender == ethereum_payment_registry, 'Only PAYMENT_REGISTRY can call'); require(_orders_pending[order_id], 'Order claimed or nonexistent'); - Order memory current_order = _orders[order_id]; //TODO check if order is memory or calldata + ERC20Order memory current_order = _orders_erc20[order_id]; //TODO check if order is memory or calldata require(current_order.recipient_address == recipient_address, 'recipient_address not match L1'); require(current_order.amount == amount, 'amount not match L1'); + require(current_order.l1_erc20_address == l1_erc20_address, 'l1_erc20_address does not match L1'); _orders_pending[order_id] = false; - uint256 payment_amount = current_order.amount + current_order.fee; // TODO check overflow - (bool success,) = payable(address(uint160(mm_zksync_wallet))).call{value: payment_amount}(""); - require(success, "Transfer failed."); + //amount + fee in ETH: + IERC20(current_order.l2_erc20_address).safeTransfer(mm_zksync_wallet, amount); //will revert if failed + (bool success,) = payable(address(uint160(mm_zksync_wallet))).call{value: current_order.fee}(""); + require(success, "Fee transfer failed."); - emit ClaimPayment(order_id, mm_zksync_wallet, amount); + emit ClaimPaymentERC20(order_id, mm_zksync_wallet, amount, current_order.l2_erc20_address); + } + + function get_order(uint256 order_id) public view returns (Order memory) { + return _orders[order_id]; + } + + //Function recieves in msg.value the total value, and in fee the user specifies what portion of that msg.value is fee for MM + function set_order(address recipient_address, uint256 fee) public payable whenNotPaused returns (uint256) { + require(msg.value > 0, 'some ETH must be sent'); + require(msg.value > fee, 'ETH sent must be more than fee'); + + uint256 bridge_amount = msg.value - fee; //no underflow since previous check is made + + Order memory new_order = Order({recipient_address: recipient_address, amount: bridge_amount, fee: fee}); + _orders[_current_order_id] = new_order; + _orders_pending[_current_order_id] = true; + _orders_senders[_current_order_id] = msg.sender; + _orders_timestamps[_current_order_id] = block.timestamp; + _current_order_id++; //this here to follow CEI pattern + + emit SetOrder(_current_order_id-1, recipient_address, bridge_amount, fee); + + return _current_order_id-1; } // l1 handler - function claim_payment_ERC20( + function claim_payment( uint256 order_id, address recipient_address, - uint256 amount, - address erc20Address + uint256 amount ) public whenNotPaused { require(msg.sender == ethereum_payment_registry, 'Only PAYMENT_REGISTRY can call'); require(_orders_pending[order_id], 'Order claimed or nonexistent'); - ERC20Order memory current_order = _orders_erc20[order_id]; //TODO check if order is memory or calldata + Order memory current_order = _orders[order_id]; //TODO check if order is memory or calldata require(current_order.recipient_address == recipient_address, 'recipient_address not match L1'); require(current_order.amount == amount, 'amount not match L1'); - require(current_order.erc20Address == erc20Address, 'erc20Address does not match L1'); _orders_pending[order_id] = false; + uint256 payment_amount = current_order.amount + current_order.fee; // TODO check overflow - //amount + fee in ETH: - IERC20(erc20Address).safeTransfer(mm_zksync_wallet, current_order.amount); //will revert if failed - (bool success,) = payable(address(uint160(mm_zksync_wallet))).call{value: current_order.fee}(""); - require(success, "Fee transfer failed."); + (bool success,) = payable(address(uint160(mm_zksync_wallet))).call{value: payment_amount}(""); + require(success, "Transfer failed."); - emit ClaimPaymentERC20(order_id, mm_zksync_wallet, amount, erc20Address); + emit ClaimPayment(order_id, mm_zksync_wallet, amount); } // l1 handler From 7b0d5a07258137a29bd19c18c9c9328e2066003b Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Tue, 16 Apr 2024 16:10:48 -0300 Subject: [PATCH 10/48] refactor: comments for tidyness --- contracts/ethereum/src/PaymentRegistry.sol | 29 ++++++---------------- contracts/zksync/contracts/escrow.sol | 5 ++-- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/contracts/ethereum/src/PaymentRegistry.sol b/contracts/ethereum/src/PaymentRegistry.sol index 1f501c88..b0c931a6 100644 --- a/contracts/ethereum/src/PaymentRegistry.sol +++ b/contracts/ethereum/src/PaymentRegistry.sol @@ -79,6 +79,7 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { marketMaker = marketMaker_; } + //I think this is not OK. here, the caller of safeIncreaseAllowance isPaymentRegistry function registerMMerc20Allowance(address erc20) external onlyOwnerOrMM() { IERC20(erc20).safeIncreaseAllowance(address(this), type(uint256).max); } @@ -88,7 +89,6 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { // // If paid in ETH: // // It is easy for MM to calculate how much fee is desirable for him to bridge the tokens // // But An extra tx is needed containing this gas. - // // // actually the same tx could contain the mm fee in --value // // it is more expensive // // If paid in ERC20: @@ -97,31 +97,18 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { // // No extra tx is needed to pay this gas fee, MM will simply transfer less ERC20 tokens than what he recieved. // // It is cheaper - - // Transfer process: - // analyzed alternative: - // transfer from MM to PaymentRegistry - // transfer from PaymentRegistry to User - // // This solution makes 2 ERC20 transfers, very expensive and unnecessary - // IERC20(erc20Address).safeTransferFrom(msg.sender, PaymentRegistry, amount); //this needs allowance - // IERC20(erc20Address).safeTransfer(destAddress, amount); //from this contract. should recieve the erc20 tokens first. Calling a TransferFrom(MM, to YAB) - - // Better alternative: - // directly transfer from MM to User - // // This solution seems appropriate. TransferFrom was built for exactly this. - - // before transferFrom I should check funds + allowance? or safeTransfer checks this? - // // Funds: - // // We shouldnt check for funds: MM shouldn't call Transfer if he doesnt have enough ERC20 tokens for the bridge. - // // Allowance: - // // We have only 1 MM, we should allow PaymentRegistry to send all MM tokens. Set PaymentRegistry allowance of MM to unlimited. // // This unlimited allowance should be set in a separate function. MM will allow to brdige x or y ERC20. // // // we could even find new uses for this allowance. PaymentRegistry could be more intertwined with MM. Maybe automatically doing transfers in its name. require(amount > 0, "Invalid amount, should be higher than 0."); - require(IERC20(l1_erc20_address).balanceOf(msg.sender) >= amount, "MM has insufficient balance"); - require(IERC20(l1_erc20_address).allowance(msg.sender, address(this)) >= amount, "PaymentRegistry has insufficient allowance"); + + // these 2 checks are made and reverted accordingly by SafeTransfer + // but if made now, if reverted, user doesnt spend the gas of calculating keccak + // I think appropriate users should not pay for shitty users's mustakes + // require(IERC20(l1_erc20_address).balanceOf(msg.sender) >= amount, "MM has insufficient balance"); + // require(IERC20(l1_erc20_address).allowance(msg.sender, address(this)) >= amount, "PaymentRegistry has insufficient allowance"); + //TODO check if there is a way to increment allowance directly from here, i think there is not. bytes32 index = keccak256(abi.encodePacked(orderId, destAddress, amount, chainId, l1_erc20_address)); //added erc20Address diff --git a/contracts/zksync/contracts/escrow.sol b/contracts/zksync/contracts/escrow.sol index 0c2b5899..0ea9e47a 100644 --- a/contracts/zksync/contracts/escrow.sol +++ b/contracts/zksync/contracts/escrow.sol @@ -61,9 +61,8 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, // FUNCTIONS : + //I think this is not OK. here, the caller of safeIncreaseAllowance isPaymentRegistry function increase_erc20_allowance(address l2_erc20_address, uint256 amount) public whenNotPaused { - //this is wrongggggg, i think there is no way of increasing allowance like this - // F IERC20(l2_erc20_address).safeIncreaseAllowance(msg.sender, amount); //do allow max amount? same price to execute and saves a posterior need of execution } @@ -106,7 +105,7 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, _orders_pending[order_id] = false; - //amount + fee in ETH: + //transfer amount + fee in ETH: IERC20(current_order.l2_erc20_address).safeTransfer(mm_zksync_wallet, amount); //will revert if failed (bool success,) = payable(address(uint160(mm_zksync_wallet))).call{value: current_order.fee}(""); require(success, "Fee transfer failed."); From 7a7ace4f1b17812a58492a76ad6e8b38aca346d4 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Wed, 17 Apr 2024 12:43:35 -0300 Subject: [PATCH 11/48] refactor: ER20Order -> OrderERC20 --- contracts/zksync/contracts/escrow.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/zksync/contracts/escrow.sol b/contracts/zksync/contracts/escrow.sol index 0ea9e47a..0dce0490 100644 --- a/contracts/zksync/contracts/escrow.sol +++ b/contracts/zksync/contracts/escrow.sol @@ -22,7 +22,7 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, uint256 fee; } - struct ERC20Order { + struct OrderERC20 { address recipient_address; uint256 amount; uint256 fee; @@ -34,13 +34,13 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, event SetOrderERC20(uint256 order_id, address recipient_address, uint256 amount, uint256 fee, address l1_erc20_address, address l2_erc20_address); event ClaimPayment(uint256 order_id, address claimer_address, uint256 amount); - event ClaimPaymentERC20(uint256 order_id, address claimer_address, uint256 amount, address l1_erc20_address); + event ClaimPaymentERC20(uint256 order_id, address claimer_address, uint256 amount, address l2_erc20_address); //storage uint256 private _current_order_id; mapping(uint256 => Order) private _orders; - mapping(uint256 => ERC20Order) private _orders_erc20; + mapping(uint256 => OrderERC20) private _orders_erc20; mapping(uint256 => bool) private _orders_pending; mapping(uint256 => address) private _orders_senders; mapping(uint256 => uint256) private _orders_timestamps; @@ -76,7 +76,7 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, require(IERC20(l2_erc20_address).allowance(msg.sender, address(this)) >= amount, "Escrow has insuficient allowance"); IERC20(l2_erc20_address).safeTransferFrom(msg.sender, address(this), amount); //will revert if failed - ERC20Order memory new_order = ERC20Order({recipient_address: recipient_address, amount: amount, fee: msg.value, l1_erc20_address: l1_erc20_address, l2_erc20_address: l2_erc20_address}); + OrderERC20 memory new_order = OrderERC20({recipient_address: recipient_address, amount: amount, fee: msg.value, l1_erc20_address: l1_erc20_address, l2_erc20_address: l2_erc20_address}); _orders_erc20[_current_order_id] = new_order; _orders_pending[_current_order_id] = true; _orders_senders[_current_order_id] = msg.sender; @@ -98,7 +98,7 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, require(msg.sender == ethereum_payment_registry, 'Only PAYMENT_REGISTRY can call'); require(_orders_pending[order_id], 'Order claimed or nonexistent'); - ERC20Order memory current_order = _orders_erc20[order_id]; //TODO check if order is memory or calldata + OrderERC20 memory current_order = _orders_erc20[order_id]; //TODO check if order is memory or calldata require(current_order.recipient_address == recipient_address, 'recipient_address not match L1'); require(current_order.amount == amount, 'amount not match L1'); require(current_order.l1_erc20_address == l1_erc20_address, 'l1_erc20_address does not match L1'); From e4f962b46af5414024f928a5d4e695df63cab35e Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Wed, 17 Apr 2024 12:46:07 -0300 Subject: [PATCH 12/48] feat: add get_order_erc20 in ZKSync Escrow --- contracts/zksync/contracts/escrow.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/zksync/contracts/escrow.sol b/contracts/zksync/contracts/escrow.sol index 0dce0490..4da31d63 100644 --- a/contracts/zksync/contracts/escrow.sol +++ b/contracts/zksync/contracts/escrow.sol @@ -117,6 +117,10 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, return _orders[order_id]; } + function get_order_erc20(uint256 order_id) public view returns (OrderERC20 memory) { + return _orders_erc20[order_id]; + } + //Function recieves in msg.value the total value, and in fee the user specifies what portion of that msg.value is fee for MM function set_order(address recipient_address, uint256 fee) public payable whenNotPaused returns (uint256) { require(msg.value > 0, 'some ETH must be sent'); From 9b42c7894f6ad377e2ae9742c7bd8db98a3bed39 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Wed, 17 Apr 2024 12:57:28 -0300 Subject: [PATCH 13/48] feat: set_order_erc20 in SN Escrow --- contracts/starknet/src/escrow.cairo | 86 ++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/contracts/starknet/src/escrow.cairo b/contracts/starknet/src/escrow.cairo index 60cdf1c4..d3b2fa70 100644 --- a/contracts/starknet/src/escrow.cairo +++ b/contracts/starknet/src/escrow.cairo @@ -7,11 +7,21 @@ struct Order { fee: u256 } +#[derive(Copy, Drop, Serde, starknet::Store)] +struct OrderERC20 { + recipient_address: EthAddress, + amount: u256, + fee: u256, + l1_erc20_address: EthAddress, + l2_erc20_address: ContractAddress +} + #[starknet::interface] trait IEscrow { fn get_order(self: @ContractState, order_id: u256) -> Order; fn set_order(ref self: ContractState, order: Order) -> u256; + fn set_order_erc20(ref self: ContractState, order: OrderERC20) -> u256; fn get_order_pending(self: @ContractState, order_id: u256) -> bool; @@ -32,7 +42,7 @@ trait IEscrow { #[starknet::contract] mod Escrow { use core::traits::Into; -use super::{IEscrow, Order}; + use super::{IEscrow, Order, OrderERC20}; use openzeppelin::{ access::ownable::OwnableComponent, @@ -77,7 +87,9 @@ use super::{IEscrow, Order}; #[derive(Drop, starknet::Event)] enum Event { ClaimPayment: ClaimPayment, + ClaimPaymentERC20: ClaimPaymentERC20, SetOrder: SetOrder, + SetOrderERC20: SetOrderERC20, #[flat] OwnableEvent: OwnableComponent::Event, #[flat] @@ -94,6 +106,16 @@ use super::{IEscrow, Order}; fee: u256 } + #[derive(Drop, starknet::Event)] + struct SetOrderERC20 { + order_id: u256, + recipient_address: EthAddress, + amount: u256, + fee: u256, + l1_erc20_address: EthAddress, + l2_erc20_address: ContractAddress + } + #[derive(Drop, starknet::Event)] struct ClaimPayment { order_id: u256, @@ -101,10 +123,19 @@ use super::{IEscrow, Order}; amount: u256, } + #[derive(Drop, starknet::Event)] + struct ClaimPaymentERC20 { + order_id: u256, + address: ContractAddress, + amount: u256, + l2_erc20_address: ContractAddress + } + #[storage] struct Storage { current_order_id: u256, orders: LegacyMap::, + orders_erc20: LegacyMap::, orders_pending: LegacyMap::, orders_senders: LegacyMap::, orders_timestamps: LegacyMap::, @@ -152,6 +183,10 @@ use super::{IEscrow, Order}; self.orders.read(order_id) } + fn get_order_erc20(self: @ContractState, order_id: u256) -> OrderERC20 { + self.orders_erc20.read(order_id) + } + fn set_order(ref self: ContractState, order: Order) -> u256 { self.pausable.assert_not_paused(); assert(order.amount > 0, 'Amount must be greater than 0'); @@ -183,6 +218,46 @@ use super::{IEscrow, Order}; order_id } + fn set_order_erc20(ref self: ContractState, order_erc20: OrderERC20) -> u256 { + self.pausable.assert_not_paused(); + assert(order_erc20.amount > 0, 'Amount must be greater than 0'); //in ERC20 + assert(order_erc20.fee > 0, 'Fee must be greater than 0'); //in ETH + + // Fee (ETH): + let eth_dispatcher = IERC20Dispatcher { contract_address: self.native_token_eth_starknet.read() }; + assert(eth_dispatcher.allowance(get_caller_address(), get_contract_address()) >= order_erc20.fee, 'Not enough allowance for fee'); + assert(eth_dispatcher.balanceOf(get_caller_address()) >= order_erc20.fee, 'Not enough balance for fee'); + + // Amount (ERC20): + let erc20_dispatcher = IERC20Dispatcher { contract_address: order.l2_erc20_address.read() }; + assert(erc20_dispatcher.allowance(get_caller_address(), get_contract_address()) >= order_erc20.amount, 'Not enough allowance for amount'); + assert(erc20_dispatcher.balanceOf(get_caller_address()) >= order_erc20.amount, 'Not enough balance for amount'); + + let mut order_id = self.current_order_id.read(); + self.orders_erc20.write(order_id, order_erc20); + self.orders_pending.write(order_id, true); + self.orders_senders.write(order_id, get_caller_address()); + self.orders_timestamps.write(order_id, get_block_timestamp()); + self.current_order_id.write(order_id + 1); + + eth_dispatcher.transferFrom(get_caller_address(), get_contract_address(), order_erc20.fee); + erc20_dispatcher.transferFrom(get_caller_address(), get_contract_address(), order_erc20.amount); + + self + .emit( + SetOrderERC20 { + order_id, + recipient_address: order.recipient_address, + amount: order.amount, + fee: order.fee, + l1_erc20_address: order.l1_erc20_address, + l2_erc20_address: order.l2_erc20_address + } + ); + + order_id + } + fn get_order_pending(self: @ContractState, order_id: u256) -> bool { self.orders_pending.read(order_id) } @@ -192,11 +267,16 @@ use super::{IEscrow, Order}; order.fee } + fn get_order_erc20_fee(self: @ContractState, order_id: u256) -> u256 { + let order_erc20: OrderERC20 = self.orders_erc20.read(order_id); + order_erc20.fee + } + fn get_eth_transfer_contract(self: @ContractState) -> EthAddress { self.eth_transfer_contract.read() } - fn get_mm_ethereum_contract(self: @ContractState) -> EthAddress { + fn get_mm_ethereum_wallet(self: @ContractState) -> EthAddress { self.mm_ethereum_wallet.read() } @@ -210,7 +290,7 @@ use super::{IEscrow, Order}; self.eth_transfer_contract.write(new_contract); } - fn set_mm_ethereum_contract(ref self: ContractState, new_contract: EthAddress) { + fn set_mm_ethereum_wallet(ref self: ContractState, new_contract: EthAddress) { self.pausable.assert_not_paused(); self.ownable.assert_only_owner(); self.mm_ethereum_wallet.write(new_contract); From 5ca92d18a207bd095b12e4a6f8d4843cb920b1bb Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Wed, 17 Apr 2024 13:09:42 -0300 Subject: [PATCH 14/48] feat: add fee to claimPaymentERC20 event --- contracts/zksync/contracts/escrow.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/zksync/contracts/escrow.sol b/contracts/zksync/contracts/escrow.sol index 4da31d63..6d0077f4 100644 --- a/contracts/zksync/contracts/escrow.sol +++ b/contracts/zksync/contracts/escrow.sol @@ -34,7 +34,7 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, event SetOrderERC20(uint256 order_id, address recipient_address, uint256 amount, uint256 fee, address l1_erc20_address, address l2_erc20_address); event ClaimPayment(uint256 order_id, address claimer_address, uint256 amount); - event ClaimPaymentERC20(uint256 order_id, address claimer_address, uint256 amount, address l2_erc20_address); + event ClaimPaymentERC20(uint256 order_id, address claimer_address, uint256 amount, uint256 fee, address l2_erc20_address); //storage @@ -110,7 +110,7 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, (bool success,) = payable(address(uint160(mm_zksync_wallet))).call{value: current_order.fee}(""); require(success, "Fee transfer failed."); - emit ClaimPaymentERC20(order_id, mm_zksync_wallet, amount, current_order.l2_erc20_address); + emit ClaimPaymentERC20(order_id, mm_zksync_wallet, amount, current_order.fee, current_order.l2_erc20_address); } function get_order(uint256 order_id) public view returns (Order memory) { From d234e697c032b1f2fd0beae16d1c6394c376336b Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Wed, 17 Apr 2024 13:13:45 -0300 Subject: [PATCH 15/48] feat: claimPaymentERC20 in SN Escrow --- contracts/starknet/src/escrow.cairo | 71 ++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 12 deletions(-) diff --git a/contracts/starknet/src/escrow.cairo b/contracts/starknet/src/escrow.cairo index d3b2fa70..278b7d6a 100644 --- a/contracts/starknet/src/escrow.cairo +++ b/contracts/starknet/src/escrow.cairo @@ -128,6 +128,7 @@ mod Escrow { order_id: u256, address: ContractAddress, amount: u256, + fee: u256, l2_erc20_address: ContractAddress } @@ -328,6 +329,22 @@ mod Escrow { _claim_payment(ref self, from_address, order_id, recipient_address, amount); } + #[l1_handler] + fn claim_payment_erc20( + ref self: ContractState, + from_address: felt252, + order_id: u256, + recipient_address: EthAddress, + amount: u256, + l1_erc20_address: EthAddress + ) { + self.pausable.assert_not_paused(); + let eth_transfer_contract_felt: felt252 = self.eth_transfer_contract.read().into(); + assert(from_address == eth_transfer_contract_felt, 'Only PAYMENT_REGISTRY_CONTRACT'); + + _claim_payment_erc20(ref self, from_address, order_id, recipient_address, amount, l1_erc20_address); + } + #[l1_handler] fn claim_payment_batch( ref self: ContractState, @@ -358,20 +375,50 @@ mod Escrow { order_id: u256, recipient_address: EthAddress, amount: u256 - ) { - assert(self.orders_pending.read(order_id), 'Order withdrew or nonexistent'); + ) { + assert(self.orders_pending.read(order_id), 'Order withdrew or nonexistent'); + + let order = self.orders.read(order_id); + assert(order.recipient_address == recipient_address, 'recipient_address not match L1'); + assert(order.amount == amount, 'amount not match L1'); + + self.orders_pending.write(order_id, false); + let payment_amount = order.amount + order.fee; + + // TODO: In batch, it might be best to transfer all at once + IERC20Dispatcher { contract_address: self.native_token_eth_starknet.read() } + .transfer(self.mm_starknet_wallet.read(), payment_amount); - let order = self.orders.read(order_id); - assert(order.recipient_address == recipient_address, 'recipient_address not match L1'); - assert(order.amount == amount, 'amount not match L1'); + self.emit(ClaimPayment { order_id, address: self.mm_starknet_wallet.read(), amount }); + } - self.orders_pending.write(order_id, false); - let payment_amount = order.amount + order.fee; + fn _claim_payment_erc20( + ref self: ContractState, + from_address: felt252, + order_id: u256, + recipient_address: EthAddress, + amount: u256, + l1_erc20_address: EthAddress + ) { + assert(self.orders_pending.read(order_id), 'Order withdrew or nonexistent'); + + let order_erc20 = self.orders_erc20.read(order_id); + assert(order_erc20.recipient_address == recipient_address, 'recipient_address not match L1'); + assert(order_erc20.amount == amount, 'amount not match L1'); + assert(order_erc20.l1_erc20_address == l1_erc20_address, 'l1_erc20_address not match L1'); - // TODO: Might be best to transfer all at once - IERC20Dispatcher { contract_address: self.native_token_eth_starknet.read() } - .transfer(self.mm_starknet_wallet.read(), payment_amount); + self.orders_pending.write(order_id, false); - self.emit(ClaimPayment { order_id, address: self.mm_starknet_wallet.read(), amount }); - } + // let payment_amount = order.amount + order.fee; + + //Fee (ETH): + IERC20Dispatcher { contract_address: self.native_token_eth_starknet.read() } + .transfer(self.mm_starknet_wallet.read(), order_erc20.fee); + + //Amount (ERC20): + IERC20Dispatcher { contract_address: order_erc20.l2_erc20_address.read() } + .transfer(self.mm_starknet_wallet.read(), order_erc20.amount); + + self.emit(ClaimPaymentERC20 { order_id, address: self.mm_starknet_wallet.read(), amount, fee: order_erc20.fee.read(), l2_erc20_address: order_erc20.l2_erc20_address.read() } ); + } } From 911afdf5922eca1e222205a8faafa2f37e190b97 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Wed, 17 Apr 2024 13:54:41 -0300 Subject: [PATCH 16/48] fix: small bugs --- contracts/starknet/src/escrow.cairo | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/contracts/starknet/src/escrow.cairo b/contracts/starknet/src/escrow.cairo index 278b7d6a..6c19a7f0 100644 --- a/contracts/starknet/src/escrow.cairo +++ b/contracts/starknet/src/escrow.cairo @@ -19,20 +19,22 @@ struct OrderERC20 { #[starknet::interface] trait IEscrow { fn get_order(self: @ContractState, order_id: u256) -> Order; + fn get_order_erc20(self: @ContractState, order_id: u256) -> OrderERC20; fn set_order(ref self: ContractState, order: Order) -> u256; - fn set_order_erc20(ref self: ContractState, order: OrderERC20) -> u256; + fn set_order_erc20(ref self: ContractState, order_erc20: OrderERC20) -> u256; fn get_order_pending(self: @ContractState, order_id: u256) -> bool; fn get_order_fee(self: @ContractState, order_id: u256) -> u256; + fn get_order_erc20_fee(self: @ContractState, order_id: u256) -> u256; fn get_eth_transfer_contract(self: @ContractState) -> EthAddress; - fn get_mm_ethereum_contract(self: @ContractState) -> EthAddress; + fn get_mm_ethereum_wallet(self: @ContractState) -> EthAddress; fn get_mm_starknet_contract(self: @ContractState) -> ContractAddress; fn set_eth_transfer_contract(ref self: ContractState, new_contract: EthAddress); - fn set_mm_ethereum_contract(ref self: ContractState, new_contract: EthAddress); + fn set_mm_ethereum_wallet(ref self: ContractState, new_contract: EthAddress); fn set_mm_starknet_contract(ref self: ContractState, new_contract: ContractAddress); fn pause(ref self: ContractState); @@ -230,7 +232,7 @@ mod Escrow { assert(eth_dispatcher.balanceOf(get_caller_address()) >= order_erc20.fee, 'Not enough balance for fee'); // Amount (ERC20): - let erc20_dispatcher = IERC20Dispatcher { contract_address: order.l2_erc20_address.read() }; + let erc20_dispatcher = IERC20Dispatcher { contract_address: order_erc20.l2_erc20_address }; assert(erc20_dispatcher.allowance(get_caller_address(), get_contract_address()) >= order_erc20.amount, 'Not enough allowance for amount'); assert(erc20_dispatcher.balanceOf(get_caller_address()) >= order_erc20.amount, 'Not enough balance for amount'); @@ -248,11 +250,11 @@ mod Escrow { .emit( SetOrderERC20 { order_id, - recipient_address: order.recipient_address, - amount: order.amount, - fee: order.fee, - l1_erc20_address: order.l1_erc20_address, - l2_erc20_address: order.l2_erc20_address + recipient_address: order_erc20.recipient_address, + amount: order_erc20.amount, + fee: order_erc20.fee, + l1_erc20_address: order_erc20.l1_erc20_address, + l2_erc20_address: order_erc20.l2_erc20_address } ); @@ -416,9 +418,9 @@ mod Escrow { .transfer(self.mm_starknet_wallet.read(), order_erc20.fee); //Amount (ERC20): - IERC20Dispatcher { contract_address: order_erc20.l2_erc20_address.read() } + IERC20Dispatcher { contract_address: order_erc20.l2_erc20_address } .transfer(self.mm_starknet_wallet.read(), order_erc20.amount); - self.emit(ClaimPaymentERC20 { order_id, address: self.mm_starknet_wallet.read(), amount, fee: order_erc20.fee.read(), l2_erc20_address: order_erc20.l2_erc20_address.read() } ); + self.emit(ClaimPaymentERC20 { order_id, address: self.mm_starknet_wallet.read(), amount, fee: order_erc20.fee, l2_erc20_address: order_erc20.l2_erc20_address } ); } } From a1b5ca7b5f2222a2c84e7b4d78120c3434b9ca77 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Wed, 17 Apr 2024 15:46:02 -0300 Subject: [PATCH 17/48] feat: deploy SN erc20 --- Makefile | 3 ++ contracts/starknet/scripts/deploy_erc20.sh | 58 ++++++++++++++++++++++ contracts/starknet/src/ERC20.cairo | 11 ++-- 3 files changed, 67 insertions(+), 5 deletions(-) create mode 100755 contracts/starknet/scripts/deploy_erc20.sh diff --git a/Makefile b/Makefile index bb1999b3..e338b128 100644 --- a/Makefile +++ b/Makefile @@ -78,6 +78,9 @@ starknet-pause: starknet-unpause: @. ./contracts/starknet/.env && . ./contracts/starknet/scripts/change_pause_state.sh unpause +starknet-deploy-erc20: starknet-build + @. ./contracts/starknet/.env && . ./contracts/starknet/scripts/deploy_erc20.sh + ### ZKSYNC ### diff --git a/contracts/starknet/scripts/deploy_erc20.sh b/contracts/starknet/scripts/deploy_erc20.sh new file mode 100755 index 00000000..da9a7db2 --- /dev/null +++ b/contracts/starknet/scripts/deploy_erc20.sh @@ -0,0 +1,58 @@ +#!/bin/bash +. contracts/utils/colors.sh #for ANSI colors + +export STARKNET_RPC=$STARKNET_RPC + +if [ -z "$STARKNET_ACCOUNT" ]; then + printf "\n${RED}ERROR:${COLOR_RESET}" + echo "STARKNET_ACCOUNT Variable is empty. Aborting execution.\n" + exit 1 +fi +if [ -z "$STARKNET_KEYSTORE" ] && [ -z "$STARKNET_PRIVATE_KEY" ]; then + printf "\n${RED}ERROR:${COLOR_RESET}" + echo "STARKNET_KEYSTORE and STARKNET_PRIVATE_KEY Variables are empty. Aborting execution.\n" + exit 1 +fi +if [ -z "$MM_STARKNET_WALLET_ADDRESS" ]; then + printf "\n${RED}ERROR:${COLOR_RESET}" + echo "MM_STARKNET_WALLET_ADDRESS Variable is empty. Aborting execution.\n" + exit 1 +fi + + +printf "${GREEN}\n=> [SN] Declaring ERC20${COLOR_RESET}\n" +ERC20_CLASS_HASH=$(starkli declare \ + --account $STARKNET_ACCOUNT \ + $(if [ -n "$STARKNET_KEYSTORE" ]; then echo "--keystore $STARKNET_KEYSTORE"; fi) \ + $(if [ -n "$STARKNET_PRIVATE_KEY" ]; then echo "--private-key $STARKNET_PRIVATE_KEY"; fi) \ + --watch contracts/starknet/target/dev/yab_ERC20.contract_class.json) + + +if [ -z "$ERC20_CLASS_HASH" ]; then + printf "\n${RED}ERROR:${COLOR_RESET}\n" + echo "ERC20_CLASS_HASH Variable is empty. Aborting execution.\n" + exit 1 +fi + +printf "${GREEN}\n=> [SN] ERC20 Declared${COLOR_RESET}\n" + +printf "${CYAN}[SN] ERC20 ClassHash: $ERC20_CLASS_HASH${COLOR_RESET}\n" +# maybe print initial whale +# printf "${PINK}[ETH] Market Maker ETH Wallet: $MM_ETHEREUM_WALLET_ADDRESS${COLOR_RESET}\n" +NAME='URICOIN' +SYMBOL='URI' +INITIAL_SUPPLY=1000000 # 1_000_000 +RECIPIENT=0x078557823d56a27dd29881285ae58efba18a9da536df0a0c674564e4185e7629 #Braavos account 1, user, contract address format is OK + +printf "${GREEN}\n=> [SN] Deploying ERC20${COLOR_RESET}\n" +ERC20_CONTRACT_ADDRESS=$(starkli deploy --max-fee-raw 31367442226306\ + --account $STARKNET_ACCOUNT \ + $(if [ -n "$STARKNET_KEYSTORE" ]; then echo "--keystore $STARKNET_KEYSTORE"; fi) \ + $(if [ -n "$STARKNET_PRIVATE_KEY" ]; then echo "--private-key $STARKNET_PRIVATE_KEY"; fi) \ + --watch $ERC20_CLASS_HASH \ + $RECIPIENT) +echo $ERC20_CONTRACT_ADDRESS + +printf "${GREEN}\n=> [SN] ERC20 Deployed${COLOR_RESET}\n" + +printf "${CYAN}[SN] ERC20 Address: $ERC20_CONTRACT_ADDRESS${COLOR_RESET}\n" diff --git a/contracts/starknet/src/ERC20.cairo b/contracts/starknet/src/ERC20.cairo index 0a76f9b9..a9f5843f 100644 --- a/contracts/starknet/src/ERC20.cairo +++ b/contracts/starknet/src/ERC20.cairo @@ -51,13 +51,14 @@ mod ERC20 { #[constructor] fn constructor( ref self: ContractState, - name: felt252, - symbol: felt252, - initial_supply: u256, + // name: felt252, + // symbol: felt252, + // initial_supply: u256, recipient: ContractAddress ) { - self.initializer(name, symbol); - self._mint(recipient, initial_supply); + // self.initializer(name, symbol); + self.initializer('URICOIN', 'URI'); + self._mint(recipient, 1000000); } // From d2ca63d6826ea93e28260c6fa746842cd58b9a9c Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Thu, 18 Apr 2024 12:49:04 -0300 Subject: [PATCH 18/48] feat: add claimPaymentStarknetERC20 in PaymentRegistry --- contracts/ethereum/.env.example | 1 + contracts/ethereum/.env.test | 1 + contracts/ethereum/script/Deploy.s.sol | 4 +- contracts/ethereum/src/PaymentRegistry.sol | 47 +++++++++++++++++----- 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/contracts/ethereum/.env.example b/contracts/ethereum/.env.example index f6b8f549..aff8645b 100644 --- a/contracts/ethereum/.env.example +++ b/contracts/ethereum/.env.example @@ -10,6 +10,7 @@ ZKSYNC_DIAMOND_PROXY_ADDRESS=<0x9A6DE0f62Aa270A8bCB1e2610078650D539B1Ef9> # Sepo STARKNET_CLAIM_PAYMENT_SELECTOR=<0x03636c566f6409560d55d5f6d1eb4ee163b096b4698c503e69e210be79de2afa> #hex value of starknet's claim_payment selector STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR=<0x0354a01e49fe07e43306a97ed84dbd5de8238c7d8ff616caa3444630cfc559e6> #hex value of starknet's claim_payment_batch selector +STARKNET_CLAIM_PAYMENT_ERC20_SELECTOR=<0x0091ec2842317cd03601c3f46ee8ebc9b1dc6cdbc96cb7b0873cc6360538d754> #hex value of starknet's claim_payment_erc20 selector ZKSYNC_CLAIM_PAYMENT_SELECTOR=<0xa5168739> #hex value of ZKSync's claim_payment selctor ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR=<0x156be1ae> #hex value of ZKSync's claim_payment_batch selctor ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR=<0xb9738dd6> #hex value of ZKSync's claim_payment_erc20 selctor diff --git a/contracts/ethereum/.env.test b/contracts/ethereum/.env.test index 139b59dd..c0c649a1 100644 --- a/contracts/ethereum/.env.test +++ b/contracts/ethereum/.env.test @@ -6,6 +6,7 @@ MM_ETHEREUM_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 SKIP_VERIFY=true STARKNET_CLAIM_PAYMENT_SELECTOR=0x03636c566f6409560d55d5f6d1eb4ee163b096b4698c503e69e210be79de2afa STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR=0x0354a01e49fe07e43306a97ed84dbd5de8238c7d8ff616caa3444630cfc559e6 +STARKNET_CLAIM_PAYMENT_ERC20_SELECTOR=0x0091ec2842317cd03601c3f46ee8ebc9b1dc6cdbc96cb7b0873cc6360538d754 ZKSYNC_CLAIM_PAYMENT_SELECTOR=0xa5168739 ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR=0x156be1ae ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR=0xb9738dd6 diff --git a/contracts/ethereum/script/Deploy.s.sol b/contracts/ethereum/script/Deploy.s.sol index 06c071c9..494f1b3a 100644 --- a/contracts/ethereum/script/Deploy.s.sol +++ b/contracts/ethereum/script/Deploy.s.sol @@ -13,6 +13,7 @@ contract Deploy is Script { address STARKNET_MESSAGING_ADDRESS = vm.envAddress("STARKNET_MESSAGING_ADDRESS"); uint256 STARKNET_CLAIM_PAYMENT_SELECTOR = vm.envUint("STARKNET_CLAIM_PAYMENT_SELECTOR"); uint256 STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR = vm.envUint("STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR"); + uint256 STARKNET_CLAIM_PAYMENT_ERC20_SELECTOR = vm.envUint("STARKNET_CLAIM_PAYMENT_ERC20_SELECTOR"); address MM_ETHEREUM_WALLET_ADDRESS = vm.envAddress("MM_ETHEREUM_WALLET_ADDRESS"); address ZKSYNC_DIAMOND_PROXY_ADDRESS = vm.envAddress("ZKSYNC_DIAMOND_PROXY_ADDRESS"); bytes4 ZKSYNC_CLAIM_PAYMENT_SELECTOR = bytes4(vm.envBytes("ZKSYNC_CLAIM_PAYMENT_SELECTOR")); @@ -27,7 +28,8 @@ contract Deploy is Script { PaymentRegistry(address(proxy)).initialize( STARKNET_MESSAGING_ADDRESS, STARKNET_CLAIM_PAYMENT_SELECTOR, - STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR, + STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR, + STARKNET_CLAIM_PAYMENT_ERC20_SELECTOR, MM_ETHEREUM_WALLET_ADDRESS, ZKSYNC_DIAMOND_PROXY_ADDRESS, ZKSYNC_CLAIM_PAYMENT_SELECTOR, diff --git a/contracts/ethereum/src/PaymentRegistry.sol b/contracts/ethereum/src/PaymentRegistry.sol index b0c931a6..d7d23ea3 100644 --- a/contracts/ethereum/src/PaymentRegistry.sol +++ b/contracts/ethereum/src/PaymentRegistry.sol @@ -34,6 +34,7 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { address public ZKSyncEscrowAddress; uint256 public StarknetEscrowClaimPaymentSelector; uint256 public StarknetEscrowClaimPaymentBatchSelector; + uint256 public StarknetEscrowClaimPaymentERC20Selector; bytes4 public ZKSyncEscrowClaimPaymentSelector; bytes4 public ZKSyncEscrowClaimPaymentBatchSelector; bytes4 public ZKSyncEscrowClaimPaymentERC20Selector; @@ -54,6 +55,7 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { address snMessaging, uint256 StarknetEscrowClaimPaymentSelector_, uint256 StarknetEscrowClaimPaymentBatchSelector_, + uint256 StarknetEscrowClaimPaymentERC20Selector_, address marketMaker_, address ZKSyncDiamondProxyAddress, bytes4 ZKSyncEscrowClaimPaymentSelector_, @@ -69,6 +71,7 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { StarknetEscrowClaimPaymentSelector = StarknetEscrowClaimPaymentSelector_; StarknetEscrowClaimPaymentBatchSelector = StarknetEscrowClaimPaymentBatchSelector_; + StarknetEscrowClaimPaymentERC20Selector = StarknetEscrowClaimPaymentERC20Selector_; ZKSyncEscrowClaimPaymentSelector = ZKSyncEscrowClaimPaymentSelector_; ZKSyncEscrowClaimPaymentBatchSelector = ZKSyncEscrowClaimPaymentBatchSelector_; ZKSyncEscrowClaimPaymentERC20Selector = ZKSyncEscrowClaimPaymentERC20Selector_; @@ -79,11 +82,6 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { marketMaker = marketMaker_; } - //I think this is not OK. here, the caller of safeIncreaseAllowance isPaymentRegistry - function registerMMerc20Allowance(address erc20) external onlyOwnerOrMM() { - IERC20(erc20).safeIncreaseAllowance(address(this), type(uint256).max); - } - function transferERC20(uint256 orderId, address destAddress, uint128 chainId, address l1_erc20_address, uint256 amount) external onlyOwnerOrMM { // Decide if MM fees are paid in ERC20 or in ETH. // // If paid in ETH: @@ -150,6 +148,30 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { emit ClaimPaymentERC20(orderId, destAddress, amount, ZKSyncChainId, l1_erc20_address); } + function claimPaymentStarknetERC20( + uint256 orderId, + address destAddress, + uint256 amount, + address l1_erc20_address + ) external payable onlyOwnerOrMM { + _verifyTransferExistsStarknetERC20(orderId, destAddress, amount, l1_erc20_address); + + uint256[] memory payload = new uint256[](6); //this is not an array of u128 because sendMessageToL2 takes an array of uint256 + payload[0] = uint128(orderId); // low + payload[1] = uint128(orderId >> 128); // high + payload[2] = uint256(uint160(destAddress)); + payload[3] = uint128(amount); // low + payload[4] = uint128(amount >> 128); // high + payload[5] = uint256(uint160(l1_erc20_address)); + + _snMessaging.sendMessageToL2{value: msg.value}( + StarknetEscrowAddress, + StarknetEscrowClaimPaymentERC20Selector, //TODO set erc20 variable + payload); + + emit ClaimPaymentERC20(orderId, destAddress, amount, StarknetChainId, l1_erc20_address); + } + //TODO: change orderID to uint32 function transfer(uint256 orderId, address destAddress, uint128 chainId) external payable onlyOwnerOrMM { require(msg.value > 0, "Invalid amount, should be higher than 0."); @@ -219,11 +241,6 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { emit ClaimPaymentBatch(orderIds, destAddresses, amounts, StarknetChainId); } - function _verifyTransferExistsStarknet(uint256 orderId, address destAddress, uint256 amount) internal view { - bytes32 index = keccak256(abi.encodePacked(orderId, destAddress, amount, StarknetChainId)); - require(transfers[index] == true, "Transfer not found."); - } - function claimPaymentZKSync( uint256 orderId, address destAddress, uint256 amount, uint256 gasLimit, @@ -285,6 +302,16 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { emit ClaimPaymentBatch(orderIds, destAddresses, amounts, ZKSyncChainId); } + function _verifyTransferExistsStarknetERC20(uint256 orderId, address destAddress, uint256 amount, address l1_erc20_address) internal view { + bytes32 index = keccak256(abi.encodePacked(orderId, destAddress, amount, StarknetChainId, l1_erc20_address)); + require(transfers[index] == true, "Transfer not found."); //if this is claimed twice, Escrow will know + } + + function _verifyTransferExistsStarknet(uint256 orderId, address destAddress, uint256 amount) internal view { + bytes32 index = keccak256(abi.encodePacked(orderId, destAddress, amount, StarknetChainId)); + require(transfers[index] == true, "Transfer not found."); //if this is claimed twice, Escrow will know + } + function _verifyTransferExistsZKSyncERC20(uint256 orderId, address destAddress, uint256 amount, address l1_erc20_address) internal view { bytes32 index = keccak256(abi.encodePacked(orderId, destAddress, amount, ZKSyncChainId, l1_erc20_address)); require(transfers[index] == true, "Transfer not found."); //if this is claimed twice, Escrow will know From 9997744c6c1940152f6e14c34b56c27d3786f7e5 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Thu, 18 Apr 2024 12:49:23 -0300 Subject: [PATCH 19/48] fix: adapt PaymentRegistry unit tests --- contracts/ethereum/test/ACL.t.sol | 3 ++- contracts/ethereum/test/Transfer_Claim_SN.t.sol | 3 ++- contracts/ethereum/test/Transfer_Claim_ZKSync.t.sol | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/contracts/ethereum/test/ACL.t.sol b/contracts/ethereum/test/ACL.t.sol index adfb51ac..3f82ae4d 100644 --- a/contracts/ethereum/test/ACL.t.sol +++ b/contracts/ethereum/test/ACL.t.sol @@ -16,6 +16,7 @@ contract TransferTest is Test { address STARKNET_MESSAGING_ADDRESS = 0xde29d060D45901Fb19ED6C6e959EB22d8626708e; uint256 STARKNET_CLAIM_PAYMENT_SELECTOR = 0x15511cc3694f64379908437d6d64458dc76d02482052bfb8a5b33a72c054c77; uint256 STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR = 0x0354a01e49fe07e43306a97ed84dbd5de8238c7d8ff616caa3444630cfc559e6; + uint256 STARKNET_CLAIM_PAYMENT_ERC20_SELECTOR = 0x0091ec2842317cd03601c3f46ee8ebc9b1dc6cdbc96cb7b0873cc6360538d754; address ZKSYNC_DIAMOND_PROXY_ADDRESS = 0x2eD8eF54a16bBF721a318bd5a5C0F39Be70eaa65; bytes4 ZKSYNC_CLAIM_PAYMENT_SELECTOR = 0xa5168739; bytes4 ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR = 0x156be1ae; @@ -30,7 +31,7 @@ contract TransferTest is Test { yab = new PaymentRegistry(); proxy = new ERC1967Proxy(address(yab), ""); yab_caller = PaymentRegistry(address(proxy)); - yab_caller.initialize(STARKNET_MESSAGING_ADDRESS, STARKNET_CLAIM_PAYMENT_SELECTOR, STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR, MM_ETHEREUM_WALLET_ADDRESS, ZKSYNC_DIAMOND_PROXY_ADDRESS, ZKSYNC_CLAIM_PAYMENT_SELECTOR, ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR, ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR, STARKNET_CHAIN_ID, ZKSYNC_CHAIN_ID); + yab_caller.initialize(STARKNET_MESSAGING_ADDRESS, STARKNET_CLAIM_PAYMENT_SELECTOR, STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR, STARKNET_CLAIM_PAYMENT_ERC20_SELECTOR, MM_ETHEREUM_WALLET_ADDRESS, ZKSYNC_DIAMOND_PROXY_ADDRESS, ZKSYNC_CLAIM_PAYMENT_SELECTOR, ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR, ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR, STARKNET_CHAIN_ID, ZKSYNC_CHAIN_ID); vm.stopPrank(); } diff --git a/contracts/ethereum/test/Transfer_Claim_SN.t.sol b/contracts/ethereum/test/Transfer_Claim_SN.t.sol index 297b7f95..13b9bd57 100644 --- a/contracts/ethereum/test/Transfer_Claim_SN.t.sol +++ b/contracts/ethereum/test/Transfer_Claim_SN.t.sol @@ -18,6 +18,7 @@ contract TransferTest is Test { address STARKNET_MESSAGING_ADDRESS = 0xde29d060D45901Fb19ED6C6e959EB22d8626708e; uint256 STARKNET_CLAIM_PAYMENT_SELECTOR = 0x15511cc3694f64379908437d6d64458dc76d02482052bfb8a5b33a72c054c77; uint256 STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR = 0x0354a01e49fe07e43306a97ed84dbd5de8238c7d8ff616caa3444630cfc559e6; + uint256 STARKNET_CLAIM_PAYMENT_ERC20_SELECTOR = 0x0091ec2842317cd03601c3f46ee8ebc9b1dc6cdbc96cb7b0873cc6360538d754; address ZKSYNC_DIAMOND_PROXY_ADDRESS = 0x2eD8eF54a16bBF721a318bd5a5C0F39Be70eaa65; bytes4 ZKSYNC_CLAIM_PAYMENT_SELECTOR = 0xa5168739; bytes4 ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR = 0x156be1ae; @@ -32,7 +33,7 @@ contract TransferTest is Test { yab = new PaymentRegistry(); proxy = new ERC1967Proxy(address(yab), ""); yab_caller = PaymentRegistry(address(proxy)); - yab_caller.initialize(STARKNET_MESSAGING_ADDRESS, STARKNET_CLAIM_PAYMENT_SELECTOR, STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR, MM_ETHEREUM_WALLET_ADDRESS, ZKSYNC_DIAMOND_PROXY_ADDRESS, ZKSYNC_CLAIM_PAYMENT_SELECTOR, ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR, ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR, STARKNET_CHAIN_ID, ZKSYNC_CHAIN_ID); + yab_caller.initialize(STARKNET_MESSAGING_ADDRESS, STARKNET_CLAIM_PAYMENT_SELECTOR, STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR, STARKNET_CLAIM_PAYMENT_ERC20_SELECTOR, MM_ETHEREUM_WALLET_ADDRESS, ZKSYNC_DIAMOND_PROXY_ADDRESS, ZKSYNC_CLAIM_PAYMENT_SELECTOR, ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR, ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR, STARKNET_CHAIN_ID, ZKSYNC_CHAIN_ID); // Mock calls to Starknet Messaging contract vm.mockCall( STARKNET_MESSAGING_ADDRESS, diff --git a/contracts/ethereum/test/Transfer_Claim_ZKSync.t.sol b/contracts/ethereum/test/Transfer_Claim_ZKSync.t.sol index d393e978..4dcc8373 100644 --- a/contracts/ethereum/test/Transfer_Claim_ZKSync.t.sol +++ b/contracts/ethereum/test/Transfer_Claim_ZKSync.t.sol @@ -17,6 +17,7 @@ contract TransferTest is Test { address STARKNET_MESSAGING_ADDRESS = 0xde29d060D45901Fb19ED6C6e959EB22d8626708e; uint256 STARKNET_CLAIM_PAYMENT_SELECTOR = 0x15511cc3694f64379908437d6d64458dc76d02482052bfb8a5b33a72c054c77; uint256 STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR = 0x0354a01e49fe07e43306a97ed84dbd5de8238c7d8ff616caa3444630cfc559e6; + uint256 STARKNET_CLAIM_PAYMENT_ERC20_SELECTOR = 0x0091ec2842317cd03601c3f46ee8ebc9b1dc6cdbc96cb7b0873cc6360538d754; address ZKSYNC_DIAMOND_PROXY_ADDRESS = 0x2eD8eF54a16bBF721a318bd5a5C0F39Be70eaa65; bytes4 ZKSYNC_CLAIM_PAYMENT_SELECTOR = 0xa5168739; bytes4 ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR = 0x156be1ae; @@ -31,7 +32,7 @@ contract TransferTest is Test { yab = new PaymentRegistry(); proxy = new ERC1967Proxy(address(yab), ""); yab_caller = PaymentRegistry(address(proxy)); - yab_caller.initialize(STARKNET_MESSAGING_ADDRESS, STARKNET_CLAIM_PAYMENT_SELECTOR, STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR, MM_ETHEREUM_WALLET_ADDRESS, ZKSYNC_DIAMOND_PROXY_ADDRESS, ZKSYNC_CLAIM_PAYMENT_SELECTOR, ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR, ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR, STARKNET_CHAIN_ID, ZKSYNC_CHAIN_ID); + yab_caller.initialize(STARKNET_MESSAGING_ADDRESS, STARKNET_CLAIM_PAYMENT_SELECTOR, STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR, STARKNET_CLAIM_PAYMENT_ERC20_SELECTOR, MM_ETHEREUM_WALLET_ADDRESS, ZKSYNC_DIAMOND_PROXY_ADDRESS, ZKSYNC_CLAIM_PAYMENT_SELECTOR, ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR, ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR, STARKNET_CHAIN_ID, ZKSYNC_CHAIN_ID); //Mock calls to ZKSync Mailbox contract vm.mockCall( ZKSYNC_DIAMOND_PROXY_ADDRESS, From d9ea7942a701809335e3271888f7d8a9d0313308 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Thu, 18 Apr 2024 12:50:53 -0300 Subject: [PATCH 20/48] feat: split 'amount' into 'amount_l1' and 'amount_l2' in ZKSync escrow --- contracts/zksync/contracts/escrow.sol | 45 ++++++++++++++------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/contracts/zksync/contracts/escrow.sol b/contracts/zksync/contracts/escrow.sol index 6d0077f4..dce0e192 100644 --- a/contracts/zksync/contracts/escrow.sol +++ b/contracts/zksync/contracts/escrow.sol @@ -24,17 +24,18 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, struct OrderERC20 { address recipient_address; - uint256 amount; + uint256 amount_l2; + uint256 amount_l1; uint256 fee; - address l1_erc20_address; address l2_erc20_address; + address l1_erc20_address; } event SetOrder(uint256 order_id, address recipient_address, uint256 amount, uint256 fee); - event SetOrderERC20(uint256 order_id, address recipient_address, uint256 amount, uint256 fee, address l1_erc20_address, address l2_erc20_address); + event SetOrderERC20(uint256 order_id, address recipient_address, uint256 amount_l2, uint256 amount_l1, uint256 fee, address l2_erc20_address, address l1_erc20_address); event ClaimPayment(uint256 order_id, address claimer_address, uint256 amount); - event ClaimPaymentERC20(uint256 order_id, address claimer_address, uint256 amount, uint256 fee, address l2_erc20_address); + event ClaimPaymentERC20(uint256 order_id, address claimer_address, uint256 amount_l2, uint256 fee, address l2_erc20_address); //storage @@ -61,29 +62,29 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, // FUNCTIONS : - //I think this is not OK. here, the caller of safeIncreaseAllowance isPaymentRegistry - function increase_erc20_allowance(address l2_erc20_address, uint256 amount) public whenNotPaused { - IERC20(l2_erc20_address).safeIncreaseAllowance(msg.sender, amount); //do allow max amount? same price to execute and saves a posterior need of execution - } - - //Function recieves in msg.value the total fee for MM, and in amout the total tokens he wants to bridge - function set_order_erc20(address recipient_address, uint256 amount, address l1_erc20_address, address l2_erc20_address) public payable whenNotPaused returns (uint256) { + // Recieves in msg.value the total fee for MM + // in amount_l2, the total tokens he will give to MM in L2 + // in amount_l1, the total tokens he will receive from MM in L1 + // this way, the user is able to bridge tokens cross-erc20, giving, for example, WETH and recieving USDC + // the extra computational costs of this is neglegible, only 1 extra param and 1 extra uint256 stored per ERC20 order in L2, and NO EXTRA COSTS in L1 + function set_order_erc20(address recipient_address, uint256 amount_l2, uint256 amount_l1, address l2_erc20_address, address l1_erc20_address) public payable whenNotPaused returns (uint256) { require(msg.value > 0, 'some ETH must be sent as MM fees'); - require(amount > 0, 'some tokens must be sent to bridge'); + require(amount_l2 > 0, 'some tokens must be sent to MM in L2'); + require(amount_l1 > 0, 'some tokens must be sent to MM in L1'); //the following needs allowance, which is not set automatically - require(IERC20(l2_erc20_address).balanceOf(msg.sender) >= amount, "User has insuficient funds"); - require(IERC20(l2_erc20_address).allowance(msg.sender, address(this)) >= amount, "Escrow has insuficient allowance"); - IERC20(l2_erc20_address).safeTransferFrom(msg.sender, address(this), amount); //will revert if failed + require(IERC20(l2_erc20_address).balanceOf(msg.sender) >= amount_l2, "User has insuficient funds"); + require(IERC20(l2_erc20_address).allowance(msg.sender, address(this)) >= amount_l2, "Escrow has insuficient allowance"); + IERC20(l2_erc20_address).safeTransferFrom(msg.sender, address(this), amount_l2); //will revert if failed - OrderERC20 memory new_order = OrderERC20({recipient_address: recipient_address, amount: amount, fee: msg.value, l1_erc20_address: l1_erc20_address, l2_erc20_address: l2_erc20_address}); + OrderERC20 memory new_order = OrderERC20({recipient_address: recipient_address, amount_l2: amount_l2, amount_l1: amount_l1, fee: msg.value, l2_erc20_address: l2_erc20_address, l1_erc20_address: l1_erc20_address}); _orders_erc20[_current_order_id] = new_order; _orders_pending[_current_order_id] = true; _orders_senders[_current_order_id] = msg.sender; _orders_timestamps[_current_order_id] = block.timestamp; _current_order_id++; //this here to follow CEI pattern - emit SetOrderERC20(_current_order_id-1, recipient_address, amount, msg.value, l1_erc20_address, l2_erc20_address); + emit SetOrderERC20(_current_order_id-1, recipient_address, amount_l2, amount_l1, msg.value, l2_erc20_address, l1_erc20_address); return _current_order_id-1; } @@ -92,7 +93,7 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, function claim_payment_erc20( uint256 order_id, address recipient_address, - uint256 amount, + uint256 amount_l1, address l1_erc20_address ) public whenNotPaused { require(msg.sender == ethereum_payment_registry, 'Only PAYMENT_REGISTRY can call'); @@ -100,17 +101,17 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, OrderERC20 memory current_order = _orders_erc20[order_id]; //TODO check if order is memory or calldata require(current_order.recipient_address == recipient_address, 'recipient_address not match L1'); - require(current_order.amount == amount, 'amount not match L1'); - require(current_order.l1_erc20_address == l1_erc20_address, 'l1_erc20_address does not match L1'); + require(current_order.amount_l1 == amount_l1, 'amount_l1 not match L1'); + require(current_order.l1_erc20_address == l1_erc20_address, 'l1_erc20_address not match L1'); _orders_pending[order_id] = false; //transfer amount + fee in ETH: - IERC20(current_order.l2_erc20_address).safeTransfer(mm_zksync_wallet, amount); //will revert if failed + IERC20(current_order.l2_erc20_address).safeTransfer(mm_zksync_wallet, current_order.amount_l2); //will revert if failed (bool success,) = payable(address(uint160(mm_zksync_wallet))).call{value: current_order.fee}(""); require(success, "Fee transfer failed."); - emit ClaimPaymentERC20(order_id, mm_zksync_wallet, amount, current_order.fee, current_order.l2_erc20_address); + emit ClaimPaymentERC20(order_id, mm_zksync_wallet, current_order.amount_l2, current_order.fee, current_order.l2_erc20_address); } function get_order(uint256 order_id) public view returns (Order memory) { From 741e91142cc5372a00a0bffd4a665795d5a80130 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Thu, 18 Apr 2024 13:09:45 -0300 Subject: [PATCH 21/48] feat: split 'amount' to 'amount_l2' + 'amount_l1' in SN escrow.cairo --- contracts/starknet/src/escrow.cairo | 54 +++++++++++++++------------ contracts/zksync/contracts/escrow.sol | 2 +- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/contracts/starknet/src/escrow.cairo b/contracts/starknet/src/escrow.cairo index 6c19a7f0..af84d61d 100644 --- a/contracts/starknet/src/escrow.cairo +++ b/contracts/starknet/src/escrow.cairo @@ -10,10 +10,11 @@ struct Order { #[derive(Copy, Drop, Serde, starknet::Store)] struct OrderERC20 { recipient_address: EthAddress, - amount: u256, + amount_l2: u256, + amount_l1: u256, fee: u256, - l1_erc20_address: EthAddress, - l2_erc20_address: ContractAddress + l2_erc20_address: ContractAddress, + l1_erc20_address: EthAddress } #[starknet::interface] @@ -112,10 +113,11 @@ mod Escrow { struct SetOrderERC20 { order_id: u256, recipient_address: EthAddress, - amount: u256, + amount_l2: u256, + amount_l1: u256, fee: u256, - l1_erc20_address: EthAddress, - l2_erc20_address: ContractAddress + l2_erc20_address: ContractAddress, + l1_erc20_address: EthAddress } #[derive(Drop, starknet::Event)] @@ -129,7 +131,7 @@ mod Escrow { struct ClaimPaymentERC20 { order_id: u256, address: ContractAddress, - amount: u256, + amount_l2: u256, fee: u256, l2_erc20_address: ContractAddress } @@ -142,7 +144,7 @@ mod Escrow { orders_pending: LegacyMap::, orders_senders: LegacyMap::, orders_timestamps: LegacyMap::, - eth_transfer_contract: EthAddress, // our transfer contract in L1 + eth_transfer_contract: EthAddress, // our transfer (PaymentRegistry) contract in L1 mm_ethereum_wallet: EthAddress, mm_starknet_wallet: ContractAddress, native_token_eth_starknet: ContractAddress, @@ -221,9 +223,16 @@ mod Escrow { order_id } + + // Recieves in order.fee the total fee for MM + // in order.amount_l2, the total tokens he will give to MM in L2 + // in order.amount_l1, the total tokens he will receive from MM in L1 + // this way, the user is able to bridge tokens cross-erc20, giving, for example, WETH and recieving USDC + // the extra computational costs of this is neglegible: only 1 extra param and 1 extra uint256 stored per ERC20 order in L2, and NO EXTRA COSTS in L1 fn set_order_erc20(ref self: ContractState, order_erc20: OrderERC20) -> u256 { self.pausable.assert_not_paused(); - assert(order_erc20.amount > 0, 'Amount must be greater than 0'); //in ERC20 + assert(order_erc20.amount_l2 > 0, 'Amount_l2 must be greater than 0'); //in ERC20 + assert(order_erc20.amount_l1 > 0, 'Amount_l1 must be greater than 0'); //in ERC20 assert(order_erc20.fee > 0, 'Fee must be greater than 0'); //in ETH // Fee (ETH): @@ -233,8 +242,8 @@ mod Escrow { // Amount (ERC20): let erc20_dispatcher = IERC20Dispatcher { contract_address: order_erc20.l2_erc20_address }; - assert(erc20_dispatcher.allowance(get_caller_address(), get_contract_address()) >= order_erc20.amount, 'Not enough allowance for amount'); - assert(erc20_dispatcher.balanceOf(get_caller_address()) >= order_erc20.amount, 'Not enough balance for amount'); + assert(erc20_dispatcher.allowance(get_caller_address(), get_contract_address()) >= order_erc20.amount_l2, 'Not enough allowance for amount_l2'); + assert(erc20_dispatcher.balanceOf(get_caller_address()) >= order_erc20.amount_l2, 'Not enough balance for amount_l2'); let mut order_id = self.current_order_id.read(); self.orders_erc20.write(order_id, order_erc20); @@ -244,17 +253,18 @@ mod Escrow { self.current_order_id.write(order_id + 1); eth_dispatcher.transferFrom(get_caller_address(), get_contract_address(), order_erc20.fee); - erc20_dispatcher.transferFrom(get_caller_address(), get_contract_address(), order_erc20.amount); + erc20_dispatcher.transferFrom(get_caller_address(), get_contract_address(), order_erc20.amount_l2); self .emit( SetOrderERC20 { order_id, recipient_address: order_erc20.recipient_address, - amount: order_erc20.amount, + amount_l2: order_erc20.amount_l2, + amount_l1: order_erc20.amount_l1, fee: order_erc20.fee, - l1_erc20_address: order_erc20.l1_erc20_address, - l2_erc20_address: order_erc20.l2_erc20_address + l2_erc20_address: order_erc20.l2_erc20_address, + l1_erc20_address: order_erc20.l1_erc20_address } ); @@ -337,14 +347,14 @@ mod Escrow { from_address: felt252, order_id: u256, recipient_address: EthAddress, - amount: u256, + amount_l1: u256, l1_erc20_address: EthAddress ) { self.pausable.assert_not_paused(); let eth_transfer_contract_felt: felt252 = self.eth_transfer_contract.read().into(); assert(from_address == eth_transfer_contract_felt, 'Only PAYMENT_REGISTRY_CONTRACT'); - _claim_payment_erc20(ref self, from_address, order_id, recipient_address, amount, l1_erc20_address); + _claim_payment_erc20(ref self, from_address, order_id, recipient_address, amount_l1, l1_erc20_address); } #[l1_handler] @@ -399,28 +409,26 @@ mod Escrow { from_address: felt252, order_id: u256, recipient_address: EthAddress, - amount: u256, + amount_l1: u256, l1_erc20_address: EthAddress ) { assert(self.orders_pending.read(order_id), 'Order withdrew or nonexistent'); let order_erc20 = self.orders_erc20.read(order_id); assert(order_erc20.recipient_address == recipient_address, 'recipient_address not match L1'); - assert(order_erc20.amount == amount, 'amount not match L1'); + assert(order_erc20.amount_l1 == amount_l1, 'amount_l1 not match L1'); assert(order_erc20.l1_erc20_address == l1_erc20_address, 'l1_erc20_address not match L1'); self.orders_pending.write(order_id, false); - // let payment_amount = order.amount + order.fee; - //Fee (ETH): IERC20Dispatcher { contract_address: self.native_token_eth_starknet.read() } .transfer(self.mm_starknet_wallet.read(), order_erc20.fee); //Amount (ERC20): IERC20Dispatcher { contract_address: order_erc20.l2_erc20_address } - .transfer(self.mm_starknet_wallet.read(), order_erc20.amount); + .transfer(self.mm_starknet_wallet.read(), order_erc20.amount_l2); - self.emit(ClaimPaymentERC20 { order_id, address: self.mm_starknet_wallet.read(), amount, fee: order_erc20.fee, l2_erc20_address: order_erc20.l2_erc20_address } ); + self.emit(ClaimPaymentERC20 { order_id, address: self.mm_starknet_wallet.read(), amount_l2: order_erc20.amount_l2, fee: order_erc20.fee, l2_erc20_address: order_erc20.l2_erc20_address } ); } } diff --git a/contracts/zksync/contracts/escrow.sol b/contracts/zksync/contracts/escrow.sol index dce0e192..bfad8921 100644 --- a/contracts/zksync/contracts/escrow.sol +++ b/contracts/zksync/contracts/escrow.sol @@ -66,7 +66,7 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, // in amount_l2, the total tokens he will give to MM in L2 // in amount_l1, the total tokens he will receive from MM in L1 // this way, the user is able to bridge tokens cross-erc20, giving, for example, WETH and recieving USDC - // the extra computational costs of this is neglegible, only 1 extra param and 1 extra uint256 stored per ERC20 order in L2, and NO EXTRA COSTS in L1 + // the extra computational costs of this is neglegible: only 1 extra param and 1 extra uint256 stored per ERC20 order in L2, and NO EXTRA COSTS in L1 function set_order_erc20(address recipient_address, uint256 amount_l2, uint256 amount_l1, address l2_erc20_address, address l1_erc20_address) public payable whenNotPaused returns (uint256) { require(msg.value > 0, 'some ETH must be sent as MM fees'); require(amount_l2 > 0, 'some tokens must be sent to MM in L2'); From 1842d4af4a6ae1893be80e249b5113d8442b1cae Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Thu, 18 Apr 2024 13:12:14 -0300 Subject: [PATCH 22/48] refactor: now make deploy-all builds everything before deploying --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index e338b128..8289efbc 100644 --- a/Makefile +++ b/Makefile @@ -151,15 +151,15 @@ ethereum-and-starknet-deploy: deploy-all: @. ./contracts/ethereum/.env && . ./contracts/starknet/.env && . ./contracts/zksync/.env && \ make ethereum-build && \ - . ./contracts/ethereum/deploy.sh && \ make starknet-build && \ + make zksync-build && \ + . ./contracts/ethereum/deploy.sh && \ . ./contracts/starknet/scripts/deploy.sh && \ . ./contracts/ethereum/set_starknet_escrow.sh && \ - . ./contracts/utils/display_info.sh && \ - make zksync-build && \ . ./contracts/zksync/deploy.sh && \ - . ./contracts/ethereum/set_zksync_escrow.sh - + . ./contracts/ethereum/set_zksync_escrow.sh && \ + . ./contracts/utils/display_info.sh + test: make starknet-test make ethereum-test From ac245e9311fe15beaed75342b7b8af6b8243a06a Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Thu, 18 Apr 2024 13:15:48 -0300 Subject: [PATCH 23/48] refactor: escrow.cairo revert messages --- contracts/starknet/src/escrow.cairo | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/contracts/starknet/src/escrow.cairo b/contracts/starknet/src/escrow.cairo index af84d61d..c750be5d 100644 --- a/contracts/starknet/src/escrow.cairo +++ b/contracts/starknet/src/escrow.cairo @@ -194,7 +194,7 @@ mod Escrow { fn set_order(ref self: ContractState, order: Order) -> u256 { self.pausable.assert_not_paused(); - assert(order.amount > 0, 'Amount must be greater than 0'); + assert(order.amount > 0, 'Amount must > 0'); let payment_amount = order.amount + order.fee; let dispatcher = IERC20Dispatcher { contract_address: self.native_token_eth_starknet.read() }; @@ -231,19 +231,19 @@ mod Escrow { // the extra computational costs of this is neglegible: only 1 extra param and 1 extra uint256 stored per ERC20 order in L2, and NO EXTRA COSTS in L1 fn set_order_erc20(ref self: ContractState, order_erc20: OrderERC20) -> u256 { self.pausable.assert_not_paused(); - assert(order_erc20.amount_l2 > 0, 'Amount_l2 must be greater than 0'); //in ERC20 - assert(order_erc20.amount_l1 > 0, 'Amount_l1 must be greater than 0'); //in ERC20 - assert(order_erc20.fee > 0, 'Fee must be greater than 0'); //in ETH + assert(order_erc20.amount_l2 > 0, 'Amount_l2 must > 0'); //in ERC20 + assert(order_erc20.amount_l1 > 0, 'Amount_l1 must > 0'); //in ERC20 + assert(order_erc20.fee > 0, 'Fee must > 0'); //in ETH // Fee (ETH): let eth_dispatcher = IERC20Dispatcher { contract_address: self.native_token_eth_starknet.read() }; - assert(eth_dispatcher.allowance(get_caller_address(), get_contract_address()) >= order_erc20.fee, 'Not enough allowance for fee'); - assert(eth_dispatcher.balanceOf(get_caller_address()) >= order_erc20.fee, 'Not enough balance for fee'); + assert(eth_dispatcher.allowance(get_caller_address(), get_contract_address()) >= order_erc20.fee, 'Need allowance for fee'); + assert(eth_dispatcher.balanceOf(get_caller_address()) >= order_erc20.fee, 'Need balance for fee'); // Amount (ERC20): let erc20_dispatcher = IERC20Dispatcher { contract_address: order_erc20.l2_erc20_address }; - assert(erc20_dispatcher.allowance(get_caller_address(), get_contract_address()) >= order_erc20.amount_l2, 'Not enough allowance for amount_l2'); - assert(erc20_dispatcher.balanceOf(get_caller_address()) >= order_erc20.amount_l2, 'Not enough balance for amount_l2'); + assert(erc20_dispatcher.allowance(get_caller_address(), get_contract_address()) >= order_erc20.amount_l2, 'Need allowance for amount_l2'); + assert(erc20_dispatcher.balanceOf(get_caller_address()) >= order_erc20.amount_l2, 'Need balance for amount_l2'); let mut order_id = self.current_order_id.read(); self.orders_erc20.write(order_id, order_erc20); From 98409421a277e355944258de5f573c32a92a0a2a Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Thu, 18 Apr 2024 15:49:21 -0300 Subject: [PATCH 24/48] test: start ZKSync unit tests --- contracts/zksync/test/main.test.ts | 54 ++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/contracts/zksync/test/main.test.ts b/contracts/zksync/test/main.test.ts index 2f4007f1..b3e011ac 100644 --- a/contracts/zksync/test/main.test.ts +++ b/contracts/zksync/test/main.test.ts @@ -199,3 +199,57 @@ describe('Claim payment batch tests', function () { }); }); +describe('ERC20 Set Order tests', function () { + // it("Should emit correct Event", async () => { + // const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value}); + + // await expect(setOrderTx) + // .to.emit(escrow, "SetOrder").withArgs(0, user_eth, value-fee, fee) + // }); + // it("Should get the order setted", async () => { + // const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value}); + + // await expect(setOrderTx) + // .to.emit(escrow, "SetOrder").withArgs(0, user_eth, value-fee, fee) + + // const newOrder = await escrow.get_order(0); + + // expect(newOrder[0]).to.eq(user_eth.address); //recipient_address + // expect(Number(newOrder[1])).to.eq(value-fee); //amount + // expect(Number(newOrder[2])).to.eq(fee); //fee + // }) + // it("Should get the pending order", async () => { + // const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value}); + // await setOrderTx.wait(); + + // expect(await escrow.is_order_pending(0)).to.equal(true); + // }) + // it("Should not get the pending order", async () => { + // expect(await escrow.is_order_pending(0)).to.equal(false); + // }) +}); + +describe('ERC20 Claim Payment tests', function () { + // it("Should allow PaymentRegistry to claim payment", async () => { + // let mm_init_balance = await provider.getBalance(escrow.mm_zksync_wallet()); + + // const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value}); + // await setOrderTx.wait(); + + // const claimPaymentTx = await escrow.connect(paymentRegistry).claim_payment(0, user_eth, value-fee); + // await claimPaymentTx.wait(); + + // let mm_final_balance = await provider.getBalance(escrow.mm_zksync_wallet()); + + // expect(mm_final_balance - mm_init_balance).to.equals(value); + // }); + + // it("Should not allow PaymentRegistry to claim unexisting payment", async () => { + // expect(escrow.connect(paymentRegistry).claim_payment(0, user_eth, value-fee)).to.be.revertedWith("Order claimed or nonexistent"); + // }); + + // it("Should not allow random user to call claim payment", async () => { + // expect(escrow.connect(user_zk).claim_payment(0, user_eth, value)).to.be.revertedWith("Only PAYMENT_REGISTRY can call"); + // }); +}) + From 6a9251dfc448f5c0c420087e4219622dfad08b31 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Thu, 18 Apr 2024 16:03:40 -0300 Subject: [PATCH 25/48] refactor: migrating ZKSync erc20 unit tests to its own ts file --- contracts/zksync/test/erc20.test.ts | 80 +++++++++++++++++++++++++++++ contracts/zksync/test/main.test.ts | 55 -------------------- 2 files changed, 80 insertions(+), 55 deletions(-) create mode 100644 contracts/zksync/test/erc20.test.ts diff --git a/contracts/zksync/test/erc20.test.ts b/contracts/zksync/test/erc20.test.ts new file mode 100644 index 00000000..b13227e4 --- /dev/null +++ b/contracts/zksync/test/erc20.test.ts @@ -0,0 +1,80 @@ +import { expect } from 'chai'; +import { deployAndInit } from './utils'; +import { Contract, Fragment, Wallet, Provider } from 'ethers'; +import { getWallet, deployContract, LOCAL_RICH_WALLETS, getProvider } from '../deploy/utils'; + +let provider: Provider; +let escrow: Contract; +let paymentRegistry: Wallet = getWallet(LOCAL_RICH_WALLETS[3].privateKey); //its Wallet data type because I will mock calls from this addr + +let deployer: Wallet = getWallet(LOCAL_RICH_WALLETS[0].privateKey); +let user_zk: Wallet = getWallet(LOCAL_RICH_WALLETS[1].privateKey); +let user_zk2: Wallet = getWallet(LOCAL_RICH_WALLETS[2].privateKey); +let user_eth: Wallet = getWallet(LOCAL_RICH_WALLETS[1].privateKey); +let user_eth2: Wallet = getWallet(LOCAL_RICH_WALLETS[2].privateKey); + + + +const fee = 1; //TODO check, maybe make fuzz +const value = 10; //TODO check, maybe make fuzz + +beforeEach( async () => { + escrow = await deployAndInit(); + erc20 = await deployContract("ERC20", deployer, [1000]); + provider = getProvider(); +}); + +describe('ERC20 Set Order tests', function () { + // it("Should emit correct Event", async () => { + // const setOrderTx = await escrow.connect(user_zk).set_order_erc20(user_eth, fee, {value}); + + // await expect(setOrderTx) + // .to.emit(escrow, "SetOrder").withArgs(0, user_eth, value-fee, fee) + // }); + // it("Should get the order setted", async () => { + // const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value}); + + // await expect(setOrderTx) + // .to.emit(escrow, "SetOrder").withArgs(0, user_eth, value-fee, fee) + + // const newOrder = await escrow.get_order(0); + + // expect(newOrder[0]).to.eq(user_eth.address); //recipient_address + // expect(Number(newOrder[1])).to.eq(value-fee); //amount + // expect(Number(newOrder[2])).to.eq(fee); //fee + // }) + // it("Should get the pending order", async () => { + // const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value}); + // await setOrderTx.wait(); + + // expect(await escrow.is_order_pending(0)).to.equal(true); + // }) + // it("Should not get the pending order", async () => { + // expect(await escrow.is_order_pending(0)).to.equal(false); + // }) +}); + +describe('ERC20 Claim Payment tests', function () { + // it("Should allow PaymentRegistry to claim payment", async () => { + // let mm_init_balance = await provider.getBalance(escrow.mm_zksync_wallet()); + + // const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value}); + // await setOrderTx.wait(); + + // const claimPaymentTx = await escrow.connect(paymentRegistry).claim_payment(0, user_eth, value-fee); + // await claimPaymentTx.wait(); + + // let mm_final_balance = await provider.getBalance(escrow.mm_zksync_wallet()); + + // expect(mm_final_balance - mm_init_balance).to.equals(value); + // }); + + // it("Should not allow PaymentRegistry to claim unexisting payment", async () => { + // expect(escrow.connect(paymentRegistry).claim_payment(0, user_eth, value-fee)).to.be.revertedWith("Order claimed or nonexistent"); + // }); + + // it("Should not allow random user to call claim payment", async () => { + // expect(escrow.connect(user_zk).claim_payment(0, user_eth, value)).to.be.revertedWith("Only PAYMENT_REGISTRY can call"); + // }); +}) + diff --git a/contracts/zksync/test/main.test.ts b/contracts/zksync/test/main.test.ts index b3e011ac..1ab2bf90 100644 --- a/contracts/zksync/test/main.test.ts +++ b/contracts/zksync/test/main.test.ts @@ -198,58 +198,3 @@ describe('Claim payment batch tests', function () { await expect(escrow.connect(user_eth).claim_payment_batch([0], [user_eth.address], [value-fee])).to.be.revertedWith("Only PAYMENT_REGISTRY can call"); }); }); - -describe('ERC20 Set Order tests', function () { - // it("Should emit correct Event", async () => { - // const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value}); - - // await expect(setOrderTx) - // .to.emit(escrow, "SetOrder").withArgs(0, user_eth, value-fee, fee) - // }); - // it("Should get the order setted", async () => { - // const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value}); - - // await expect(setOrderTx) - // .to.emit(escrow, "SetOrder").withArgs(0, user_eth, value-fee, fee) - - // const newOrder = await escrow.get_order(0); - - // expect(newOrder[0]).to.eq(user_eth.address); //recipient_address - // expect(Number(newOrder[1])).to.eq(value-fee); //amount - // expect(Number(newOrder[2])).to.eq(fee); //fee - // }) - // it("Should get the pending order", async () => { - // const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value}); - // await setOrderTx.wait(); - - // expect(await escrow.is_order_pending(0)).to.equal(true); - // }) - // it("Should not get the pending order", async () => { - // expect(await escrow.is_order_pending(0)).to.equal(false); - // }) -}); - -describe('ERC20 Claim Payment tests', function () { - // it("Should allow PaymentRegistry to claim payment", async () => { - // let mm_init_balance = await provider.getBalance(escrow.mm_zksync_wallet()); - - // const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value}); - // await setOrderTx.wait(); - - // const claimPaymentTx = await escrow.connect(paymentRegistry).claim_payment(0, user_eth, value-fee); - // await claimPaymentTx.wait(); - - // let mm_final_balance = await provider.getBalance(escrow.mm_zksync_wallet()); - - // expect(mm_final_balance - mm_init_balance).to.equals(value); - // }); - - // it("Should not allow PaymentRegistry to claim unexisting payment", async () => { - // expect(escrow.connect(paymentRegistry).claim_payment(0, user_eth, value-fee)).to.be.revertedWith("Order claimed or nonexistent"); - // }); - - // it("Should not allow random user to call claim payment", async () => { - // expect(escrow.connect(user_zk).claim_payment(0, user_eth, value)).to.be.revertedWith("Only PAYMENT_REGISTRY can call"); - // }); -}) - From 1eb2d522f7a269a600efad3ebbfaa89d1cc8bf0f Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Thu, 18 Apr 2024 17:53:58 -0300 Subject: [PATCH 26/48] feat: make zksync-test now runs the new test file as well --- contracts/zksync/package.json | 2 +- contracts/zksync/test/erc20.test.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/contracts/zksync/package.json b/contracts/zksync/package.json index cea88e54..ad31b109 100644 --- a/contracts/zksync/package.json +++ b/contracts/zksync/package.json @@ -8,7 +8,7 @@ "deploy-devnet": "hardhat deploy-zksync --network dockerizedNode --script deploy.ts", "compile": "hardhat compile", "clean": "hardhat clean", - "test": "hardhat test --network dockerizedNode --show-stack-traces", + "test": "hardhat test --network dockerizedNode --show-stack-traces test/main.test.ts test/erc20.test.ts", "test-in-memory": "hardhat test --network inMemoryNode --show-stack-traces" }, "devDependencies": { diff --git a/contracts/zksync/test/erc20.test.ts b/contracts/zksync/test/erc20.test.ts index b13227e4..16698663 100644 --- a/contracts/zksync/test/erc20.test.ts +++ b/contracts/zksync/test/erc20.test.ts @@ -20,10 +20,12 @@ const value = 10; //TODO check, maybe make fuzz beforeEach( async () => { escrow = await deployAndInit(); - erc20 = await deployContract("ERC20", deployer, [1000]); + // erc20 = await deployContract("ERC20", deployer, [1000]); provider = getProvider(); }); + + describe('ERC20 Set Order tests', function () { // it("Should emit correct Event", async () => { // const setOrderTx = await escrow.connect(user_zk).set_order_erc20(user_eth, fee, {value}); From 7a8f372e2430bd2916ef0544b8eded2c2292042c Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Thu, 18 Apr 2024 19:25:48 -0300 Subject: [PATCH 27/48] test(wip): zksync unit tests --- contracts/zksync/contracts/ERC20_L2.sol | 10 +++- contracts/zksync/test/erc20.test.ts | 72 +++++++++++++++++++++++-- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/contracts/zksync/contracts/ERC20_L2.sol b/contracts/zksync/contracts/ERC20_L2.sol index 22fa6629..085a2e39 100644 --- a/contracts/zksync/contracts/ERC20_L2.sol +++ b/contracts/zksync/contracts/ERC20_L2.sol @@ -3,8 +3,14 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +// contract UriCoin is ERC20 { +// constructor() ERC20("UriCoin", "Uri") { +// _mint(0xB321099cf86D9BB913b891441B014c03a6CcFc54, 1000000); //hardcoded recipient + initial_supply +// } +// } + contract UriCoin is ERC20 { - constructor() ERC20("UriCoin", "Uri") { - _mint(0xB321099cf86D9BB913b891441B014c03a6CcFc54, 1000000); //hardcoded recipient + initial_supply + constructor(address initial_whale, uint256 initial_supply) ERC20("UriCoin", "Uri") { + _mint(initial_whale, initial_supply); //hardcoded recipient + initial_supply } } \ No newline at end of file diff --git a/contracts/zksync/test/erc20.test.ts b/contracts/zksync/test/erc20.test.ts index 16698663..4e6fe1ba 100644 --- a/contracts/zksync/test/erc20.test.ts +++ b/contracts/zksync/test/erc20.test.ts @@ -5,6 +5,8 @@ import { getWallet, deployContract, LOCAL_RICH_WALLETS, getProvider } from '../d let provider: Provider; let escrow: Contract; +let erc20_l2: Contract; +let erc20_l1: Contract; let paymentRegistry: Wallet = getWallet(LOCAL_RICH_WALLETS[3].privateKey); //its Wallet data type because I will mock calls from this addr let deployer: Wallet = getWallet(LOCAL_RICH_WALLETS[0].privateKey); @@ -17,27 +19,91 @@ let user_eth2: Wallet = getWallet(LOCAL_RICH_WALLETS[2].privateKey); const fee = 1; //TODO check, maybe make fuzz const value = 10; //TODO check, maybe make fuzz +const amount_l2 = 20; +const amount_l1 = 10; + +const erc20_l1_address = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; +let erc20_l2_address: string; + +const initial_erc20_balance = 1000000; beforeEach( async () => { escrow = await deployAndInit(); - // erc20 = await deployContract("ERC20", deployer, [1000]); + erc20_l2 = await deployContract("UriCoin", [user_zk.address, initial_erc20_balance], { wallet: deployer }); + erc20_l2_address = await erc20_l2.getAddress(); + provider = getProvider(); }); +//working: +// describe('ERC20 basic tests', function () { +// it("Should airdrop correctly", async () => { +// const balanceOf = await erc20.balanceOf(user_zk.address); +// expect(balanceOf).to.eq(initial_erc20_balance) +// }); + +// it("Should transfer correctly", async () => { +// const transferTx = await erc20.connect(user_zk).transfer(user_zk2.address, value); +// await transferTx.wait(); + +// const balanceOf = await erc20.balanceOf(user_zk2.address); +// expect(balanceOf).to.eq(value) +// }); + +// it("Should not transfer more than balance", async () => { +// expect(erc20.connect(user_zk).transfer(user_zk2.address, initial_erc20_balance+1)).to.be.revertedWith("ERC20: transfer amount exceeds balance"); +// }); + +// it("Should not transfer without allowance", async () => { +// expect(erc20.connect(user_zk2).transferFrom(user_zk.address, user_zk2.address, value)).to.be.revertedWith("ERC20: transfer amount exceeds allowance"); +// }); + +// it("Should approve correctly", async () => { +// const approveTx = await erc20.connect(user_zk).approve(user_zk2.address, value); +// await approveTx.wait(); + +// const allowance = await erc20.allowance(user_zk.address, user_zk2.address); +// expect(allowance).to.eq(value) +// }); + +// it("Should transferFrom correctly", async () => { +// const approveTx = await erc20.connect(user_zk).approve(user_zk2.address, value); +// await approveTx.wait(); + +// const transferFromTx = await erc20.connect(user_zk2).transferFrom(user_zk.address, user_zk2.address, value); +// await transferFromTx.wait(); + +// const balanceOf = await erc20.balanceOf(user_zk2.address); +// expect(balanceOf).to.eq(value) +// }); +// }) describe('ERC20 Set Order tests', function () { + it("Should emit correct Event", async () => { + const approveTx = await erc20_l2.connect(user_zk).approve(await erc20_l2.getAddress(), amount_l2); + await approveTx.wait(); + + const allowance = await erc20_l2.allowance(user_zk.address, await erc20_l2.getAddress()); + expect(allowance).to.eq(amount_l2) + + //contract caller is not being correctly set. + const setOrderTx = await escrow.connect(user_zk).set_order_erc20(user_eth.address, amount_l2, amount_l1, await erc20_l2.getAddress(), erc20_l1_address, {value: fee}); + + await expect(setOrderTx) + .to.emit(escrow, "SetOrder").withArgs(0, user_eth.address, amount_l2, amount_l1, fee, erc20_l2_address, erc20_l1_address); + }); // it("Should emit correct Event", async () => { // const setOrderTx = await escrow.connect(user_zk).set_order_erc20(user_eth, fee, {value}); // await expect(setOrderTx) - // .to.emit(escrow, "SetOrder").withArgs(0, user_eth, value-fee, fee) + // .to.emit(escrow, "SetOrder").withArgs(0, user_eth, value-fee, fee); // }); // it("Should get the order setted", async () => { // const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value}); // await expect(setOrderTx) - // .to.emit(escrow, "SetOrder").withArgs(0, user_eth, value-fee, fee) + // .to.emit(escrow, "SetOrder").withArgs(0, user_eth, value-fee, fee); // const newOrder = await escrow.get_order(0); From fd0a786dd667a3c02b4f368ecae214146bcfe979 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:43:57 -0300 Subject: [PATCH 28/48] test: finished zksync unit tests --- contracts/zksync/contracts/escrow.sol | 1 - contracts/zksync/test/erc20.test.ts | 200 +++++++++++++++----------- 2 files changed, 116 insertions(+), 85 deletions(-) diff --git a/contracts/zksync/contracts/escrow.sol b/contracts/zksync/contracts/escrow.sol index bfad8921..2ac789bf 100644 --- a/contracts/zksync/contracts/escrow.sol +++ b/contracts/zksync/contracts/escrow.sol @@ -85,7 +85,6 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //}, _current_order_id++; //this here to follow CEI pattern emit SetOrderERC20(_current_order_id-1, recipient_address, amount_l2, amount_l1, msg.value, l2_erc20_address, l1_erc20_address); - return _current_order_id-1; } diff --git a/contracts/zksync/test/erc20.test.ts b/contracts/zksync/test/erc20.test.ts index 4e6fe1ba..c7a8c7e4 100644 --- a/contracts/zksync/test/erc20.test.ts +++ b/contracts/zksync/test/erc20.test.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; import { deployAndInit } from './utils'; -import { Contract, Fragment, Wallet, Provider } from 'ethers'; +import { Contract, Fragment, Wallet, Provider, AddressLike } from 'ethers'; import { getWallet, deployContract, LOCAL_RICH_WALLETS, getProvider } from '../deploy/utils'; let provider: Provider; @@ -23,126 +23,158 @@ const amount_l2 = 20; const amount_l1 = 10; const erc20_l1_address = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; -let erc20_l2_address: string; +let erc20_l2_address: AddressLike; +let escrow_address: AddressLike; const initial_erc20_balance = 1000000; beforeEach( async () => { escrow = await deployAndInit(); + escrow_address = await escrow.getAddress(); + erc20_l2 = await deployContract("UriCoin", [user_zk.address, initial_erc20_balance], { wallet: deployer }); erc20_l2_address = await erc20_l2.getAddress(); provider = getProvider(); }); -//working: -// describe('ERC20 basic tests', function () { -// it("Should airdrop correctly", async () => { -// const balanceOf = await erc20.balanceOf(user_zk.address); -// expect(balanceOf).to.eq(initial_erc20_balance) -// }); -// it("Should transfer correctly", async () => { -// const transferTx = await erc20.connect(user_zk).transfer(user_zk2.address, value); -// await transferTx.wait(); +describe('ERC20 basic tests', function () { + it("Should airdrop correctly", async () => { + const balanceOf = await erc20.balanceOf(user_zk.address); + expect(balanceOf).to.eq(initial_erc20_balance) + }); -// const balanceOf = await erc20.balanceOf(user_zk2.address); -// expect(balanceOf).to.eq(value) -// }); + it("Should transfer correctly", async () => { + const transferTx = await erc20.connect(user_zk).transfer(user_zk2.address, value); + await transferTx.wait(); -// it("Should not transfer more than balance", async () => { -// expect(erc20.connect(user_zk).transfer(user_zk2.address, initial_erc20_balance+1)).to.be.revertedWith("ERC20: transfer amount exceeds balance"); -// }); + const balanceOf = await erc20.balanceOf(user_zk2.address); + expect(balanceOf).to.eq(value) + }); -// it("Should not transfer without allowance", async () => { -// expect(erc20.connect(user_zk2).transferFrom(user_zk.address, user_zk2.address, value)).to.be.revertedWith("ERC20: transfer amount exceeds allowance"); -// }); + it("Should not transfer more than balance", async () => { + expect(erc20.connect(user_zk).transfer(user_zk2.address, initial_erc20_balance+1)).to.be.revertedWith("ERC20: transfer amount exceeds balance"); + }); -// it("Should approve correctly", async () => { -// const approveTx = await erc20.connect(user_zk).approve(user_zk2.address, value); -// await approveTx.wait(); + it("Should not transfer without allowance", async () => { + expect(erc20.connect(user_zk2).transferFrom(user_zk.address, user_zk2.address, value)).to.be.revertedWith("ERC20: transfer amount exceeds allowance"); + }); -// const allowance = await erc20.allowance(user_zk.address, user_zk2.address); -// expect(allowance).to.eq(value) -// }); + it("Should approve correctly", async () => { + const approveTx = await erc20.connect(user_zk).approve(user_zk2.address, value); + await approveTx.wait(); + + const allowance = await erc20.allowance(user_zk.address, user_zk2.address); + expect(allowance).to.eq(value) + }); -// it("Should transferFrom correctly", async () => { -// const approveTx = await erc20.connect(user_zk).approve(user_zk2.address, value); -// await approveTx.wait(); + it("Should transferFrom correctly", async () => { + const approveTx = await erc20.connect(user_zk).approve(user_zk2.address, value); + await approveTx.wait(); -// const transferFromTx = await erc20.connect(user_zk2).transferFrom(user_zk.address, user_zk2.address, value); -// await transferFromTx.wait(); + const transferFromTx = await erc20.connect(user_zk2).transferFrom(user_zk.address, user_zk2.address, value); + await transferFromTx.wait(); -// const balanceOf = await erc20.balanceOf(user_zk2.address); -// expect(balanceOf).to.eq(value) -// }); -// }) + const balanceOf = await erc20.balanceOf(user_zk2.address); + expect(balanceOf).to.eq(value) + }); +}) describe('ERC20 Set Order tests', function () { it("Should emit correct Event", async () => { - const approveTx = await erc20_l2.connect(user_zk).approve(await erc20_l2.getAddress(), amount_l2); + const approveTx = await erc20_l2.connect(user_zk).approve(escrow_address, amount_l2); await approveTx.wait(); - const allowance = await erc20_l2.allowance(user_zk.address, await erc20_l2.getAddress()); - expect(allowance).to.eq(amount_l2) - - //contract caller is not being correctly set. - const setOrderTx = await escrow.connect(user_zk).set_order_erc20(user_eth.address, amount_l2, amount_l1, await erc20_l2.getAddress(), erc20_l1_address, {value: fee}); + const setOrderTx = await escrow.connect(user_zk).set_order_erc20(user_eth.address, amount_l2, amount_l1, erc20_l2_address, erc20_l1_address, {value: fee}); await expect(setOrderTx) - .to.emit(escrow, "SetOrder").withArgs(0, user_eth.address, amount_l2, amount_l1, fee, erc20_l2_address, erc20_l1_address); + .to.emit(escrow, "SetOrderERC20").withArgs(0, user_eth.address, amount_l2, amount_l1, fee, erc20_l2_address, erc20_l1_address); }); - // it("Should emit correct Event", async () => { - // const setOrderTx = await escrow.connect(user_zk).set_order_erc20(user_eth, fee, {value}); - - // await expect(setOrderTx) - // .to.emit(escrow, "SetOrder").withArgs(0, user_eth, value-fee, fee); - // }); - // it("Should get the order setted", async () => { - // const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value}); - - // await expect(setOrderTx) - // .to.emit(escrow, "SetOrder").withArgs(0, user_eth, value-fee, fee); - - // const newOrder = await escrow.get_order(0); - - // expect(newOrder[0]).to.eq(user_eth.address); //recipient_address - // expect(Number(newOrder[1])).to.eq(value-fee); //amount - // expect(Number(newOrder[2])).to.eq(fee); //fee - // }) - // it("Should get the pending order", async () => { - // const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value}); - // await setOrderTx.wait(); - - // expect(await escrow.is_order_pending(0)).to.equal(true); - // }) - // it("Should not get the pending order", async () => { - // expect(await escrow.is_order_pending(0)).to.equal(false); - // }) + it("Should get the order setted", async () => { + const approveTx = await erc20_l2.connect(user_zk).approve(escrow_address, amount_l2); + await approveTx.wait(); + + const setOrderTx = await escrow.connect(user_zk).set_order_erc20(user_eth.address, amount_l2, amount_l1, erc20_l2_address, erc20_l1_address, {value: fee}); + await setOrderTx.wait(); + + const newOrder = await escrow.get_order_erc20(0); + + expect(newOrder[0]).to.eq(user_eth.address); //recipient_address + expect(Number(newOrder[1])).to.eq(amount_l2); //amount_l2 + expect(Number(newOrder[2])).to.eq(amount_l1); //amount_l1 + expect(Number(newOrder[3])).to.eq(fee); //fee + expect(newOrder[4]).to.eq(erc20_l2_address); //erc20_l2_address + expect(newOrder[5]).to.eq(erc20_l1_address); //erc20_l1_address + }) + it("Should get the pending order", async () => { + const approveTx = await erc20_l2.connect(user_zk).approve(escrow_address, amount_l2); + await approveTx.wait(); + + const setOrderTx = await escrow.connect(user_zk).set_order_erc20(user_eth.address, amount_l2, amount_l1, erc20_l2_address, erc20_l1_address, {value: fee}); + await setOrderTx.wait(); + + expect(await escrow.is_order_pending(0)).to.equal(true); + }) + it("Should not get the pending order", async () => { + expect(await escrow.is_order_pending(0)).to.equal(false); + }) }); describe('ERC20 Claim Payment tests', function () { - // it("Should allow PaymentRegistry to claim payment", async () => { - // let mm_init_balance = await provider.getBalance(escrow.mm_zksync_wallet()); + it("Should allow PaymentRegistry to claim payment", async () => { + let mm_init_balance = await erc20_l2.balanceOf(escrow.mm_zksync_wallet()); - // const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value}); - // await setOrderTx.wait(); + const approveTx = await erc20_l2.connect(user_zk).approve(escrow_address, amount_l2); + await approveTx.wait(); - // const claimPaymentTx = await escrow.connect(paymentRegistry).claim_payment(0, user_eth, value-fee); - // await claimPaymentTx.wait(); + const setOrderTx = await escrow.connect(user_zk).set_order_erc20(user_eth.address, amount_l2, amount_l1, erc20_l2_address, erc20_l1_address, {value: fee}); + await setOrderTx.wait(); - // let mm_final_balance = await provider.getBalance(escrow.mm_zksync_wallet()); + //claims the payment of a transfer of amount_l1 made to erc20_l1 + const claimPaymentTx = await escrow.connect(paymentRegistry).claim_payment_erc20(0, user_eth, amount_l1, erc20_l1_address); //unit testing the Escrow, not the PaymentRegistry's transfer/claim_payment ACL + await claimPaymentTx.wait(); - // expect(mm_final_balance - mm_init_balance).to.equals(value); - // }); + let mm_final_balance = await erc20_l2.balanceOf(escrow.mm_zksync_wallet()); - // it("Should not allow PaymentRegistry to claim unexisting payment", async () => { - // expect(escrow.connect(paymentRegistry).claim_payment(0, user_eth, value-fee)).to.be.revertedWith("Order claimed or nonexistent"); - // }); + //MM balance should increase by amount_l2 + expect(mm_final_balance - mm_init_balance).to.equals(amount_l2); + }); + + it("Should not allow PaymentRegistry to claim unexisting payment", async () => { + expect(escrow.connect(paymentRegistry).claim_payment_erc20(0, user_eth, amount_l1, erc20_l1_address)).to.be.revertedWith("Order claimed or nonexistent"); + }); + + it("Should not allow random user to call claim payment", async () => { + expect(escrow.connect(user_zk).claim_payment_erc20(0, user_eth, amount_l1, erc20_l1_address)).to.be.revertedWith("Only PAYMENT_REGISTRY can call"); + }); + + it("Should not allow PaymentRegistry to claim claimed payment", async () => { + const approveTx = await erc20_l2.connect(user_zk).approve(escrow_address, amount_l2); + await approveTx.wait(); - // it("Should not allow random user to call claim payment", async () => { - // expect(escrow.connect(user_zk).claim_payment(0, user_eth, value)).to.be.revertedWith("Only PAYMENT_REGISTRY can call"); - // }); + const setOrderTx = await escrow.connect(user_zk).set_order_erc20(user_eth.address, amount_l2, amount_l1, erc20_l2_address, erc20_l1_address, {value: fee}); + await setOrderTx.wait(); + + //claims the payment of a transfer of amount_l1 made to erc20_l1 + const claimPaymentTx = await escrow.connect(paymentRegistry).claim_payment_erc20(0, user_eth, amount_l1, erc20_l1_address); //unit testing the Escrow, not the PaymentRegistry's transfer/claim_payment ACL + await claimPaymentTx.wait(); + + expect(escrow.connect(user_zk).claim_payment_erc20(0, user_eth, amount_l1, erc20_l1_address)).to.be.revertedWith("Only PAYMENT_REGISTRY can call"); + }); + it("Should not get the pending order after being claimed", async () => { + const approveTx = await erc20_l2.connect(user_zk).approve(escrow_address, amount_l2); + await approveTx.wait(); + + const setOrderTx = await escrow.connect(user_zk).set_order_erc20(user_eth.address, amount_l2, amount_l1, erc20_l2_address, erc20_l1_address, {value: fee}); + await setOrderTx.wait(); + + //claims the payment of a transfer of amount_l1 made to erc20_l1 + const claimPaymentTx = await escrow.connect(paymentRegistry).claim_payment_erc20(0, user_eth, amount_l1, erc20_l1_address); //unit testing the Escrow, not the PaymentRegistry's transfer/claim_payment ACL + await claimPaymentTx.wait(); + + expect(await escrow.is_order_pending(0)).to.equal(false); + }); }) From 8ef3b03bb03f85942f33228fc7ad73569d24b02c Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:44:42 -0300 Subject: [PATCH 29/48] test: added new 2 unit tests on non-erc20 unit tests --- contracts/zksync/test/main.test.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/contracts/zksync/test/main.test.ts b/contracts/zksync/test/main.test.ts index 1ab2bf90..02417e27 100644 --- a/contracts/zksync/test/main.test.ts +++ b/contracts/zksync/test/main.test.ts @@ -165,6 +165,25 @@ describe('Claim Payment tests', function () { it("Should not allow random user to call claim payment", async () => { expect(escrow.connect(user_zk).claim_payment(0, user_eth, value)).to.be.revertedWith("Only PAYMENT_REGISTRY can call"); }); + it("Should not allow PaymentRegistry to claim claimed payment", async () => { + const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value}); + await setOrderTx.wait(); + + const claimPaymentTx = await escrow.connect(paymentRegistry).claim_payment(0, user_eth, value-fee); + await claimPaymentTx.wait(); + + expect(escrow.connect(paymentRegistry).claim_payment(0, user_eth, value)).to.be.revertedWith("Order claimed or nonexistent"); + }); + + it("Should not get the pending order after being claimed", async () => { + const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value}); + await setOrderTx.wait(); + + const claimPaymentTx = await escrow.connect(paymentRegistry).claim_payment(0, user_eth, value-fee); + await claimPaymentTx.wait(); + + expect(await escrow.is_order_pending(0)).to.equal(false); + }); }) From 9ad5e6ee3ee83cf77ea7c8f5e19fade546110644 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 15:22:14 -0300 Subject: [PATCH 30/48] fix: erc20_l2 name in some ZKSync unit tests --- contracts/zksync/test/erc20.test.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/contracts/zksync/test/erc20.test.ts b/contracts/zksync/test/erc20.test.ts index c7a8c7e4..502d32d2 100644 --- a/contracts/zksync/test/erc20.test.ts +++ b/contracts/zksync/test/erc20.test.ts @@ -41,42 +41,42 @@ beforeEach( async () => { describe('ERC20 basic tests', function () { it("Should airdrop correctly", async () => { - const balanceOf = await erc20.balanceOf(user_zk.address); + const balanceOf = await erc20_l2.balanceOf(user_zk.address); expect(balanceOf).to.eq(initial_erc20_balance) }); it("Should transfer correctly", async () => { - const transferTx = await erc20.connect(user_zk).transfer(user_zk2.address, value); + const transferTx = await erc20_l2.connect(user_zk).transfer(user_zk2.address, value); await transferTx.wait(); - const balanceOf = await erc20.balanceOf(user_zk2.address); + const balanceOf = await erc20_l2.balanceOf(user_zk2.address); expect(balanceOf).to.eq(value) }); it("Should not transfer more than balance", async () => { - expect(erc20.connect(user_zk).transfer(user_zk2.address, initial_erc20_balance+1)).to.be.revertedWith("ERC20: transfer amount exceeds balance"); + expect(erc20_l2.connect(user_zk).transfer(user_zk2.address, initial_erc20_balance+1)).to.be.revertedWith("ERC20: transfer amount exceeds balance"); }); it("Should not transfer without allowance", async () => { - expect(erc20.connect(user_zk2).transferFrom(user_zk.address, user_zk2.address, value)).to.be.revertedWith("ERC20: transfer amount exceeds allowance"); + expect(erc20_l2.connect(user_zk2).transferFrom(user_zk.address, user_zk2.address, value)).to.be.revertedWith("ERC20: transfer amount exceeds allowance"); }); it("Should approve correctly", async () => { - const approveTx = await erc20.connect(user_zk).approve(user_zk2.address, value); + const approveTx = await erc20_l2.connect(user_zk).approve(user_zk2.address, value); await approveTx.wait(); - const allowance = await erc20.allowance(user_zk.address, user_zk2.address); + const allowance = await erc20_l2.allowance(user_zk.address, user_zk2.address); expect(allowance).to.eq(value) }); it("Should transferFrom correctly", async () => { - const approveTx = await erc20.connect(user_zk).approve(user_zk2.address, value); + const approveTx = await erc20_l2.connect(user_zk).approve(user_zk2.address, value); await approveTx.wait(); - const transferFromTx = await erc20.connect(user_zk2).transferFrom(user_zk.address, user_zk2.address, value); + const transferFromTx = await erc20_l2.connect(user_zk2).transferFrom(user_zk.address, user_zk2.address, value); await transferFromTx.wait(); - const balanceOf = await erc20.balanceOf(user_zk2.address); + const balanceOf = await erc20_l2.balanceOf(user_zk2.address); expect(balanceOf).to.eq(value) }); }) From ec8d88f165da484a34f1b70b279fe6f3a20d67c8 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 15:44:45 -0300 Subject: [PATCH 31/48] test: add first SN unit tests --- .../src/tests/test_escrow_erc20.cairo | 172 ++++++++++++++++++ .../src/tests/test_escrow_ownable.cairo | 4 +- .../starknet/src/tests/utils/constants.cairo | 4 + 3 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 contracts/starknet/src/tests/test_escrow_erc20.cairo diff --git a/contracts/starknet/src/tests/test_escrow_erc20.cairo b/contracts/starknet/src/tests/test_escrow_erc20.cairo new file mode 100644 index 00000000..ea04f514 --- /dev/null +++ b/contracts/starknet/src/tests/test_escrow_erc20.cairo @@ -0,0 +1,172 @@ +mod Escrow { + use core::array::ArrayTrait; + use core::to_byte_array::FormatAsByteArray; + use core::serde::Serde; + use core::traits::Into; + use starknet::{EthAddress, ContractAddress}; + use integer::BoundedInt; + + use snforge_std::{declare, ContractClassTrait, L1Handler, L1HandlerTrait}; + use snforge_std::{CheatTarget, start_prank, stop_prank, start_warp, stop_warp}; + + use yab::mocks::mock_Escrow_changed_functions::{IEscrow_mock_changed_functionsDispatcher, IEscrow_mock_changed_functionsDispatcherTrait}; + use yab::mocks::mock_pausableEscrow::{IEscrow_mockPausableDispatcher, IEscrow_mockPausableDispatcherTrait}; + use yab::interfaces::IERC20::{IERC20Dispatcher, IERC20DispatcherTrait}; + use yab::escrow::{IEscrowDispatcher, IEscrowDispatcherTrait, Order, OrderERC20}; + use yab::interfaces::IEVMFactsRegistry::{ + IEVMFactsRegistryDispatcher, IEVMFactsRegistryDispatcherTrait + }; + + use yab::tests::utils::{ + constants::EscrowConstants::{ + USER, OWNER, MM_STARKNET, MM_ETHEREUM, ETH_TRANSFER_CONTRACT, ETH_USER, ETH_USER_2, ETH_USER_3 + }, + }; + + use openzeppelin::{ + upgrades::{ + UpgradeableComponent, + interface::{IUpgradeable, IUpgradeableDispatcher, IUpgradeableDispatcherTrait} + }, + }; + + fn setup() -> (IEscrowDispatcher, IERC20Dispatcher) { + setup_general_with_erc20(BoundedInt::max(), BoundedInt::max()) + } + + fn setup_general_with_erc20(balance: u256, approved: u256) -> (IEscrowDispatcher, IERC20Dispatcher){ + let eth_token = deploy_erc20('ETH', '$ETH', BoundedInt::max(), OWNER()); + let uri_token = deploy_erc20('UriCoin', '$Uri', BoundedInt::max(), USER()); + let escrow = deploy_escrow( + OWNER(), + ETH_TRANSFER_CONTRACT(), + MM_ETHEREUM(), + MM_STARKNET(), + eth_token.contract_address + ); + + start_prank(CheatTarget::One(eth_token.contract_address), OWNER()); + eth_token.transfer(USER(), balance); + stop_prank(CheatTarget::One(eth_token.contract_address)); + + start_prank(CheatTarget::One(eth_token.contract_address), USER()); + eth_token.approve(escrow.contract_address, approved); + stop_prank(CheatTarget::One(eth_token.contract_address)); + + (escrow, eth_token, uri_token) + } + + fn deploy_escrow( + escrow_owner: ContractAddress, + eth_transfer_contract: EthAddress, + mm_ethereum_contract: EthAddress, + mm_starknet_contract: ContractAddress, + native_token_eth_starknet: ContractAddress + ) -> IEscrowDispatcher { + let escrow = declare('Escrow'); + let mut calldata: Array = ArrayTrait::new(); + calldata.append(escrow_owner.into()); + calldata.append(eth_transfer_contract.into()); + calldata.append(mm_ethereum_contract.into()); + calldata.append(mm_starknet_contract.into()); + calldata.append(native_token_eth_starknet.into()); + let address = escrow.deploy(@calldata).unwrap(); + return IEscrowDispatcher { contract_address: address }; + } + + fn deploy_erc20( + name: felt252, symbol: felt252, initial_supply: u256, recipent: ContractAddress + ) -> IERC20Dispatcher { + let erc20 = declare('ERC20'); + let mut calldata = array![name, symbol]; + Serde::serialize(@initial_supply, ref calldata); + calldata.append(recipent.into()); + let address = erc20.deploy(@calldata).unwrap(); + return IERC20Dispatcher { contract_address: address }; + } + + #[test] + fn test_claim_payment_erc20() { + let (escrow, eth_token, uri_token) = setup(); + + // check balance + assert(eth_token.balanceOf(escrow.contract_address) == 0, 'init: wrong balance 1'); + assert(eth_token.balanceOf(MM_STARKNET()) == 0, 'init: wrong balance 2'); + + assert(uri_token.balanceOf(escrow.contract_address) == 0, 'init: wrong balance 3'); + assert(uri_token.balanceOf(USER()) == BoundedInt::max(), 'init: wrong balance 4'); + assert(uri_token.balanceOf(MM_STARKNET()) == 0, 'init: wrong balance 5'); + + start_prank(CheatTarget::One(escrow.contract_address), USER()); + let order_erc20 = OrderERC20 { recipient_address: ETH_USER(), amount_l2: 200, amount_l1: 100, fee: 10, l2_erc20_address: uri_token.contract_address, l1_erc20_address: L1_ERC20_ADDRESS() }; + let order_id = escrow.set_order_erc20(order_erc20); + stop_prank(CheatTarget::One(escrow.contract_address)); + + // check balance + assert(eth_token.balanceOf(escrow.contract_address) == 10, 'set_order: wrong balance '); + assert(eth_token.balanceOf(MM_STARKNET()) == 0, 'set_order: wrong balance'); + + assert(uri_token.balanceOf(escrow.contract_address) == 200, 'init: wrong balance 3'); + assert(uri_token.balanceOf(USER()) == BoundedInt::max()-200, 'init: wrong balance 4'); + assert(uri_token.balanceOf(MM_STARKNET()) == 0, 'init: wrong balance 5'); + + // check Order + assert(order_id == 0, 'wrong order_id'); + let order_save = escrow.get_order_erc20(order_id); + assert(order_erc20.recipient_address == order_save.recipient_address, 'wrong recipient_address'); + assert(order_erc20.amount_l2 == order_save.amount_l2, 'wrong amount_l2'); + assert(order_erc20.amount_l1 == order_save.amount_l1, 'wrong amount_l1'); + assert(order_erc20.fee == order_save.fee, 'wrong fee'); + assert(order_erc20.l2_erc20_address == order_save.l2_erc20_address, 'wrong l2_erc20_address'); + assert(order_erc20.l1_erc20_address == order_save.l1_erc20_address, 'wrong l1_erc20_address'); + assert(escrow.get_order_pending(order_id), 'wrong order used'); + + let mut l1_handler = L1HandlerTrait::new( + contract_address: escrow.contract_address, + function_name: 'claim_payment_erc20' + ); + + let mut payload_buffer: Array = ArrayTrait::new(); + Serde::serialize(@order_id, ref payload_buffer); + Serde::serialize(@order_erc20.recipient_address, ref payload_buffer); + Serde::serialize(@order_erc20.amount_l1, ref payload_buffer); + Serde::serialize(@order_erc20.l1_erc20_address, ref payload_buffer); + + l1_handler.from_address = ETH_TRANSFER_CONTRACT().into(); + l1_handler.payload = payload_buffer.span(); + + l1_handler.execute().expect('Failed to execute l1_handler'); + + // check Order + assert(!escrow.get_order_pending(order_id), 'wrong order used'); + // check balance + assert(eth_token.balanceOf(escrow.contract_address) == 0, 'withdraw: wrong balance'); + assert(eth_token.balanceOf(MM_STARKNET()) == 10, 'withdraw: wrong balance'); + + assert(uri_token.balanceOf(escrow.contract_address) == 0, 'init: wrong balance'); + assert(uri_token.balanceOf(MM_STARKNET()) == 200, 'init: wrong balance'); + } + + #[test] + fn test_fail_random_eth_user_calls_l1_handler() { + let (escrow, _) = setup(); + let data: Array = array![1, MM_ETHEREUM().into(), 3, L1_ERC20_ADDRESS().into(), 5]; + let mut payload_buffer: Array = ArrayTrait::new(); + data.serialize(ref payload_buffer); + let mut l1_handler = L1HandlerTrait::new( + contract_address: escrow.contract_address, + function_name: 'claim_payment_erc20', + ); + l1_handler.from_address = ETH_USER().into(); + + l1_handler.payload = payload_buffer.span(); + + // same as "Should Panic" but for the L1 handler function + match l1_handler.execute() { + Result::Ok(_) => panic_with_felt252('shouldve panicked'), + Result::Err(RevertedTransaction) => { + assert(*RevertedTransaction.panic_data.at(0) == 'Only PAYMENT_REGISTRY_CONTRACT', *RevertedTransaction.panic_data.at(0)); + } + } + } +} diff --git a/contracts/starknet/src/tests/test_escrow_ownable.cairo b/contracts/starknet/src/tests/test_escrow_ownable.cairo index ef1a9f91..77285646 100644 --- a/contracts/starknet/src/tests/test_escrow_ownable.cairo +++ b/contracts/starknet/src/tests/test_escrow_ownable.cairo @@ -118,14 +118,14 @@ mod Escrow { #[should_panic(expected: ('Caller is not the owner',))] fn test_fail_set_mm_ethereum_contract() { let (escrow, _) = setup(); - escrow.set_mm_ethereum_contract(MM_ETHEREUM()); + escrow.set_mm_ethereum_wallet(MM_ETHEREUM()); } #[test] fn test_set_mm_ethereum_contract() { let (escrow, _) = setup(); start_prank(CheatTarget::One(escrow.contract_address), OWNER()); - escrow.set_mm_ethereum_contract(MM_ETHEREUM()); + escrow.set_mm_ethereum_wallet(MM_ETHEREUM()); } #[test] diff --git a/contracts/starknet/src/tests/utils/constants.cairo b/contracts/starknet/src/tests/utils/constants.cairo index 7e9c638a..1a304e82 100644 --- a/contracts/starknet/src/tests/utils/constants.cairo +++ b/contracts/starknet/src/tests/utils/constants.cairo @@ -32,4 +32,8 @@ mod EscrowConstants { fn ETH_USER_3() -> EthAddress { 101.try_into().unwrap() } + + fn L1_ERC20_ADDRESS() -> EthAddress { + 111.try_into().unwrap() + } } From cae9223b34d2ed1c72caba432b27b7eae51086e9 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 16:53:53 -0300 Subject: [PATCH 32/48] feat: un-hardcode cairo erc20 params --- contracts/starknet/scripts/deploy_erc20.sh | 10 +++++--- contracts/starknet/src/ERC20.cairo | 25 ++++++++++++++----- .../src/tests/test_escrow_allowance.cairo | 2 +- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/contracts/starknet/scripts/deploy_erc20.sh b/contracts/starknet/scripts/deploy_erc20.sh index da9a7db2..6dd2597a 100755 --- a/contracts/starknet/scripts/deploy_erc20.sh +++ b/contracts/starknet/scripts/deploy_erc20.sh @@ -39,9 +39,10 @@ printf "${GREEN}\n=> [SN] ERC20 Declared${COLOR_RESET}\n" printf "${CYAN}[SN] ERC20 ClassHash: $ERC20_CLASS_HASH${COLOR_RESET}\n" # maybe print initial whale # printf "${PINK}[ETH] Market Maker ETH Wallet: $MM_ETHEREUM_WALLET_ADDRESS${COLOR_RESET}\n" -NAME='URICOIN' -SYMBOL='URI' -INITIAL_SUPPLY=1000000 # 1_000_000 +NAME=0x555249434f494e #'URICOIN' +SYMBOL=0x555249 #'URI' +INITIAL_SUPPLY_low=0x0f4240 # 1000000 +INITIAL_SUPPLY_high=0x0 #because INITIAL_SUPPLY is a uint256 and requires 2 params RECIPIENT=0x078557823d56a27dd29881285ae58efba18a9da536df0a0c674564e4185e7629 #Braavos account 1, user, contract address format is OK printf "${GREEN}\n=> [SN] Deploying ERC20${COLOR_RESET}\n" @@ -50,6 +51,9 @@ ERC20_CONTRACT_ADDRESS=$(starkli deploy --max-fee-raw 31367442226306\ $(if [ -n "$STARKNET_KEYSTORE" ]; then echo "--keystore $STARKNET_KEYSTORE"; fi) \ $(if [ -n "$STARKNET_PRIVATE_KEY" ]; then echo "--private-key $STARKNET_PRIVATE_KEY"; fi) \ --watch $ERC20_CLASS_HASH \ + $NAME \ + $SYMBOL \ + $INITIAL_SUPPLY_low $INITIAL_SUPPLY_high \ $RECIPIENT) echo $ERC20_CONTRACT_ADDRESS diff --git a/contracts/starknet/src/ERC20.cairo b/contracts/starknet/src/ERC20.cairo index a9f5843f..6f4234d3 100644 --- a/contracts/starknet/src/ERC20.cairo +++ b/contracts/starknet/src/ERC20.cairo @@ -48,17 +48,30 @@ mod ERC20 { const MINT_TO_ZERO: felt252 = 'ERC20: mint to 0'; } +//hardcoded one + // #[constructor] + // fn constructor( + // ref self: ContractState, + // // name: felt252, + // // symbol: felt252, + // // initial_supply: u256, + // recipient: ContractAddress + // ) { + // // self.initializer(name, symbol); + // self.initializer('URICOIN', 'URI'); + // self._mint(recipient, 1000000); + // } + #[constructor] fn constructor( ref self: ContractState, - // name: felt252, - // symbol: felt252, - // initial_supply: u256, + name: felt252, + symbol: felt252, + initial_supply: u256, recipient: ContractAddress ) { - // self.initializer(name, symbol); - self.initializer('URICOIN', 'URI'); - self._mint(recipient, 1000000); + self.initializer(name, symbol); + self._mint(recipient, initial_supply); } // diff --git a/contracts/starknet/src/tests/test_escrow_allowance.cairo b/contracts/starknet/src/tests/test_escrow_allowance.cairo index 7b9e0b37..64a73ce6 100644 --- a/contracts/starknet/src/tests/test_escrow_allowance.cairo +++ b/contracts/starknet/src/tests/test_escrow_allowance.cairo @@ -147,7 +147,7 @@ mod Escrow { stop_prank(CheatTarget::One(escrow.contract_address)); // check balance - assert(eth_token.balanceOf(escrow.contract_address) == 500, 'set_order: wrong balance '); + assert(eth_token.balanceOf(escrow.contract_address) == 500, 'set_order: wrong balance'); assert(eth_token.balanceOf(MM_STARKNET()) == 0, 'set_order: wrong balance'); } From fee6905348a3d0e4d905046febaea51c96cfe273 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 16:56:31 -0300 Subject: [PATCH 33/48] feat: add cairo unit tests to lib.cairo --- contracts/starknet/src/lib.cairo | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/starknet/src/lib.cairo b/contracts/starknet/src/lib.cairo index 2e0fd740..93ad58a3 100644 --- a/contracts/starknet/src/lib.cairo +++ b/contracts/starknet/src/lib.cairo @@ -19,6 +19,7 @@ mod tests { mod test_escrow_upgrade; mod test_escrow_ownable; mod test_escrow_claim; + mod test_escrow_erc20; mod utils { mod constants; From 3e1b57ca6a103ba8bcb3006d647acee01d872c90 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 17:00:56 -0300 Subject: [PATCH 34/48] test: fix setup_with_erc20 function name --- contracts/starknet/src/tests/test_escrow_erc20.cairo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/starknet/src/tests/test_escrow_erc20.cairo b/contracts/starknet/src/tests/test_escrow_erc20.cairo index ea04f514..97d5d6ef 100644 --- a/contracts/starknet/src/tests/test_escrow_erc20.cairo +++ b/contracts/starknet/src/tests/test_escrow_erc20.cairo @@ -30,7 +30,7 @@ mod Escrow { }, }; - fn setup() -> (IEscrowDispatcher, IERC20Dispatcher) { + fn setup_with_erc20() -> (IEscrowDispatcher, IERC20Dispatcher) { setup_general_with_erc20(BoundedInt::max(), BoundedInt::max()) } @@ -87,7 +87,7 @@ mod Escrow { #[test] fn test_claim_payment_erc20() { - let (escrow, eth_token, uri_token) = setup(); + let (escrow, eth_token, uri_token) = setup_with_erc20(); // check balance assert(eth_token.balanceOf(escrow.contract_address) == 0, 'init: wrong balance 1'); From 6aea4b98024e04936b6612dd9284420bb2cbfc50 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 17:02:14 -0300 Subject: [PATCH 35/48] test: fix setup_with_erc20 return params --- contracts/starknet/src/tests/test_escrow_erc20.cairo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/starknet/src/tests/test_escrow_erc20.cairo b/contracts/starknet/src/tests/test_escrow_erc20.cairo index 97d5d6ef..1950a562 100644 --- a/contracts/starknet/src/tests/test_escrow_erc20.cairo +++ b/contracts/starknet/src/tests/test_escrow_erc20.cairo @@ -30,11 +30,11 @@ mod Escrow { }, }; - fn setup_with_erc20() -> (IEscrowDispatcher, IERC20Dispatcher) { + fn setup_with_erc20() -> (IEscrowDispatcher, IERC20Dispatcher, IERC20Dispatcher) { setup_general_with_erc20(BoundedInt::max(), BoundedInt::max()) } - fn setup_general_with_erc20(balance: u256, approved: u256) -> (IEscrowDispatcher, IERC20Dispatcher){ + fn setup_general_with_erc20(balance: u256, approved: u256) -> (IEscrowDispatcher, IERC20Dispatcher, IERC20Dispatcher){ let eth_token = deploy_erc20('ETH', '$ETH', BoundedInt::max(), OWNER()); let uri_token = deploy_erc20('UriCoin', '$Uri', BoundedInt::max(), USER()); let escrow = deploy_escrow( From 36f6684ccc7faaa5b0b32b1d7b6914949964f9c3 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 17:04:07 -0300 Subject: [PATCH 36/48] test: small fixes on cairo test --- contracts/starknet/src/tests/test_escrow_erc20.cairo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/starknet/src/tests/test_escrow_erc20.cairo b/contracts/starknet/src/tests/test_escrow_erc20.cairo index 1950a562..2dcc9ea6 100644 --- a/contracts/starknet/src/tests/test_escrow_erc20.cairo +++ b/contracts/starknet/src/tests/test_escrow_erc20.cairo @@ -19,7 +19,7 @@ mod Escrow { use yab::tests::utils::{ constants::EscrowConstants::{ - USER, OWNER, MM_STARKNET, MM_ETHEREUM, ETH_TRANSFER_CONTRACT, ETH_USER, ETH_USER_2, ETH_USER_3 + USER, OWNER, MM_STARKNET, MM_ETHEREUM, ETH_TRANSFER_CONTRACT, ETH_USER, ETH_USER_2, ETH_USER_3, L1_ERC20_ADDRESS }, }; @@ -149,7 +149,7 @@ mod Escrow { #[test] fn test_fail_random_eth_user_calls_l1_handler() { - let (escrow, _) = setup(); + let (escrow, _, _) = setup_with_erc20(); let data: Array = array![1, MM_ETHEREUM().into(), 3, L1_ERC20_ADDRESS().into(), 5]; let mut payload_buffer: Array = ArrayTrait::new(); data.serialize(ref payload_buffer); From 18a26f99bf57e145779d87c01783adbda030b684 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 17:09:44 -0300 Subject: [PATCH 37/48] fix: add new erc20 token allowance to escrow --- contracts/starknet/src/tests/test_escrow_erc20.cairo | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contracts/starknet/src/tests/test_escrow_erc20.cairo b/contracts/starknet/src/tests/test_escrow_erc20.cairo index 2dcc9ea6..f58f78bf 100644 --- a/contracts/starknet/src/tests/test_escrow_erc20.cairo +++ b/contracts/starknet/src/tests/test_escrow_erc20.cairo @@ -37,6 +37,7 @@ mod Escrow { fn setup_general_with_erc20(balance: u256, approved: u256) -> (IEscrowDispatcher, IERC20Dispatcher, IERC20Dispatcher){ let eth_token = deploy_erc20('ETH', '$ETH', BoundedInt::max(), OWNER()); let uri_token = deploy_erc20('UriCoin', '$Uri', BoundedInt::max(), USER()); + let escrow = deploy_escrow( OWNER(), ETH_TRANSFER_CONTRACT(), @@ -53,6 +54,10 @@ mod Escrow { eth_token.approve(escrow.contract_address, approved); stop_prank(CheatTarget::One(eth_token.contract_address)); + start_prank(CheatTarget::One(uri_token.contract_address), USER()); + eth_token.approve(escrow.contract_address, approved); + stop_prank(CheatTarget::One(uri_token.contract_address)); + (escrow, eth_token, uri_token) } From 356152152fa85c95373601441f7424eb77cde32d Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 17:42:48 -0300 Subject: [PATCH 38/48] fix: avoid declaring twice an erc20 class hash --- .../src/tests/test_escrow_erc20.cairo | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/contracts/starknet/src/tests/test_escrow_erc20.cairo b/contracts/starknet/src/tests/test_escrow_erc20.cairo index f58f78bf..7a55b931 100644 --- a/contracts/starknet/src/tests/test_escrow_erc20.cairo +++ b/contracts/starknet/src/tests/test_escrow_erc20.cairo @@ -35,8 +35,7 @@ mod Escrow { } fn setup_general_with_erc20(balance: u256, approved: u256) -> (IEscrowDispatcher, IERC20Dispatcher, IERC20Dispatcher){ - let eth_token = deploy_erc20('ETH', '$ETH', BoundedInt::max(), OWNER()); - let uri_token = deploy_erc20('UriCoin', '$Uri', BoundedInt::max(), USER()); + let (eth_token, uri_token) = deploy_erc20_2('ETH', '$ETH', BoundedInt::max(), OWNER(), 'UriCoin', '$Uri', BoundedInt::max(), USER()); let escrow = deploy_escrow( OWNER(), @@ -79,15 +78,21 @@ mod Escrow { return IEscrowDispatcher { contract_address: address }; } - fn deploy_erc20( - name: felt252, symbol: felt252, initial_supply: u256, recipent: ContractAddress - ) -> IERC20Dispatcher { + fn deploy_erc20_2( + name: felt252, symbol: felt252, initial_supply: u256, recipent: ContractAddress, name2: felt252, symbol_2: felt252, initial_supply_2: u256, recipent_2: ContractAddress + ) -> (IERC20Dispatcher, IERC20Dispatcher) { let erc20 = declare('ERC20'); let mut calldata = array![name, symbol]; Serde::serialize(@initial_supply, ref calldata); calldata.append(recipent.into()); - let address = erc20.deploy(@calldata).unwrap(); - return IERC20Dispatcher { contract_address: address }; + let address_1 = erc20.deploy(@calldata).unwrap(); + + let mut calldata_2 = array![name_2, symbol_2]; + Serde::serialize(@initial_supply_2, ref calldata_2); + calldata_2.append(recipent_2.into()); + let address_2 = erc20.deploy(@calldata_2).unwrap(); + + return (IERC20Dispatcher { contract_address: address_1 }, IERC20Dispatcher { contract_address: address_2 }); } #[test] From b06978c9aa81b47ae8a6fd7dd5a42bf9e5a0401c Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 17:44:17 -0300 Subject: [PATCH 39/48] fix: deploy_erc20_2 name_2 parameter --- contracts/starknet/src/tests/test_escrow_erc20.cairo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/starknet/src/tests/test_escrow_erc20.cairo b/contracts/starknet/src/tests/test_escrow_erc20.cairo index 7a55b931..c00d4d06 100644 --- a/contracts/starknet/src/tests/test_escrow_erc20.cairo +++ b/contracts/starknet/src/tests/test_escrow_erc20.cairo @@ -78,8 +78,8 @@ mod Escrow { return IEscrowDispatcher { contract_address: address }; } - fn deploy_erc20_2( - name: felt252, symbol: felt252, initial_supply: u256, recipent: ContractAddress, name2: felt252, symbol_2: felt252, initial_supply_2: u256, recipent_2: ContractAddress + fn deploy_erc20_2( //without this, the declare('ERC20') line is called twice, causing the execution to crash + name: felt252, symbol: felt252, initial_supply: u256, recipent: ContractAddress, name_2: felt252, symbol_2: felt252, initial_supply_2: u256, recipent_2: ContractAddress ) -> (IERC20Dispatcher, IERC20Dispatcher) { let erc20 = declare('ERC20'); let mut calldata = array![name, symbol]; From fbdb42198c8f04acb4101c466e300a04e25d10e6 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 17:45:46 -0300 Subject: [PATCH 40/48] fix: add erc20 selectors to SN integration test --- .github/workflows/integration-test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index f278142d..f58da075 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -163,8 +163,10 @@ jobs: export ZKSYNC_CHAIN_ID=${{vars.ZKSYNC_CHAIN_ID}} export STARKNET_CLAIM_PAYMENT_SELECTOR=${{vars.STARKNET_CLAIM_PAYMENT_SELECTOR}} export STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR=${{vars.STARKNET_CLAIM_PAYMENT_BATCH_SELECTOR}} + export STARKNET_CLAIM_PAYMENT_ERC20_SELECTOR=${{vars.STARKNET_CLAIM_PAYMENT_ERC20_SELECTOR}} export ZKSYNC_CLAIM_PAYMENT_SELECTOR=${{vars.ZKSYNC_CLAIM_PAYMENT_SELECTOR}} export ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR=${{vars.ZKSYNC_CLAIM_PAYMENT_BATCH_SELECTOR}} + export ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR=${{vars.ZKSYNC_CLAIM_PAYMENT_ERC20_SELECTOR}} export SKIP_VERIFY=true . ./contracts/ethereum/deploy.sh From a6503734492f9912a207b3582bd675d3958758f2 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 18:08:35 -0300 Subject: [PATCH 41/48] fix: allowance for cairo escrow in test --- contracts/starknet/src/tests/test_escrow_erc20.cairo | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/starknet/src/tests/test_escrow_erc20.cairo b/contracts/starknet/src/tests/test_escrow_erc20.cairo index c00d4d06..dd5d2bc2 100644 --- a/contracts/starknet/src/tests/test_escrow_erc20.cairo +++ b/contracts/starknet/src/tests/test_escrow_erc20.cairo @@ -54,7 +54,7 @@ mod Escrow { stop_prank(CheatTarget::One(eth_token.contract_address)); start_prank(CheatTarget::One(uri_token.contract_address), USER()); - eth_token.approve(escrow.contract_address, approved); + uri_token.approve(escrow.contract_address, approved); stop_prank(CheatTarget::One(uri_token.contract_address)); (escrow, eth_token, uri_token) @@ -85,14 +85,14 @@ mod Escrow { let mut calldata = array![name, symbol]; Serde::serialize(@initial_supply, ref calldata); calldata.append(recipent.into()); - let address_1 = erc20.deploy(@calldata).unwrap(); + let address = erc20.deploy(@calldata).unwrap(); let mut calldata_2 = array![name_2, symbol_2]; Serde::serialize(@initial_supply_2, ref calldata_2); calldata_2.append(recipent_2.into()); let address_2 = erc20.deploy(@calldata_2).unwrap(); - return (IERC20Dispatcher { contract_address: address_1 }, IERC20Dispatcher { contract_address: address_2 }); + return (IERC20Dispatcher { contract_address: address }, IERC20Dispatcher { contract_address: address_2 }); } #[test] From 12f8a1d960658452e999d88a3ed53b20d4e25c33 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 18:20:13 -0300 Subject: [PATCH 42/48] test: added SN erc20 unit tests --- .../src/tests/test_escrow_erc20.cairo | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/contracts/starknet/src/tests/test_escrow_erc20.cairo b/contracts/starknet/src/tests/test_escrow_erc20.cairo index dd5d2bc2..881feb2f 100644 --- a/contracts/starknet/src/tests/test_escrow_erc20.cairo +++ b/contracts/starknet/src/tests/test_escrow_erc20.cairo @@ -157,6 +157,41 @@ mod Escrow { assert(uri_token.balanceOf(MM_STARKNET()) == 200, 'init: wrong balance'); } + #[test] + fn test_fail_claim_erc20_wrong_erc20() { + let (escrow, eth_token, uri_token) = setup_with_erc20(); + + //set order + start_prank(CheatTarget::One(escrow.contract_address), USER()); + let order_erc20 = OrderERC20 { recipient_address: ETH_USER(), amount_l2: 200, amount_l1: 100, fee: 10, l2_erc20_address: uri_token.contract_address, l1_erc20_address: L1_ERC20_ADDRESS() }; + let order_id = escrow.set_order_erc20(order_erc20); + stop_prank(CheatTarget::One(escrow.contract_address)); + + + let mut l1_handler = L1HandlerTrait::new( + contract_address: escrow.contract_address, + function_name: 'claim_payment_erc20' + ); + + //claim payment + let mut payload_buffer: Array = ArrayTrait::new(); + Serde::serialize(@order_id, ref payload_buffer); + Serde::serialize(@order_erc20.recipient_address, ref payload_buffer); + Serde::serialize(@order_erc20.amount_l1, ref payload_buffer); + Serde::serialize(@order_erc20.uri_token.contract_address, ref payload_buffer); //wrong, sent to uri_token instead of l1_erc20_address + + l1_handler.from_address = ETH_TRANSFER_CONTRACT().into(); + l1_handler.payload = payload_buffer.span(); + + // same as "Should Panic" but for the L1 handler function + match l1_handler.execute() { + Result::Ok(_) => panic_with_felt252('shouldve panicked'), + Result::Err(RevertedTransaction) => { + assert(*RevertedTransaction.panic_data.at(0) == 'l1_erc20_address not match L1', *RevertedTransaction.panic_data.at(0)); + } + } + } + #[test] fn test_fail_random_eth_user_calls_l1_handler() { let (escrow, _, _) = setup_with_erc20(); @@ -179,4 +214,33 @@ mod Escrow { } } } + + #[test] + fn test_fail_call_erc20_l1_handler_while_paused() { + let (escrow, _, _) = setup_with_erc20(); + let pausable = IPausableDispatcher { contract_address: escrow.contract_address }; + + start_prank(CheatTarget::One(escrow.contract_address), OWNER()); + escrow.pause(); + stop_prank(CheatTarget::One(escrow.contract_address)); + + let data: Array = array![1, MM_ETHEREUM().into(), 3, L1_ERC20_ADDRESS().into(), 5]; + let mut payload_buffer: Array = ArrayTrait::new(); + data.serialize(ref payload_buffer); + let mut l1_handler = L1HandlerTrait::new( + contract_address: escrow.contract_address, + function_name: 'claim_payment_erc20', + ); + l1_handler.from_address = ETH_USER().into(); + + l1_handler.payload = payload_buffer.span(); + + // same as "Should Panic" but for the L1 handler function + match l1_handler.execute() { + Result::Ok(_) => panic_with_felt252('shouldve panicked'), + Result::Err(RevertedTransaction) => { + assert(*RevertedTransaction.panic_data.at(0) == 'Pausable: paused', *RevertedTransaction.panic_data.at(0)); + } + } + } } From 32dcbea983f12bdf1dc3119285a001d87377688b Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 18:24:07 -0300 Subject: [PATCH 43/48] test: more SN erc20 unit tests --- .../src/tests/test_escrow_erc20.cairo | 40 +++++++++++-------- .../src/tests/test_escrow_pause.cairo | 31 +++++++++++++- 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/contracts/starknet/src/tests/test_escrow_erc20.cairo b/contracts/starknet/src/tests/test_escrow_erc20.cairo index 881feb2f..9de366e7 100644 --- a/contracts/starknet/src/tests/test_escrow_erc20.cairo +++ b/contracts/starknet/src/tests/test_escrow_erc20.cairo @@ -178,7 +178,7 @@ mod Escrow { Serde::serialize(@order_id, ref payload_buffer); Serde::serialize(@order_erc20.recipient_address, ref payload_buffer); Serde::serialize(@order_erc20.amount_l1, ref payload_buffer); - Serde::serialize(@order_erc20.uri_token.contract_address, ref payload_buffer); //wrong, sent to uri_token instead of l1_erc20_address + Serde::serialize(@order_erc20.l2_erc20_address, ref payload_buffer); //wrong, sent to l2_erc20_address instead of l1_erc20_address l1_handler.from_address = ETH_TRANSFER_CONTRACT().into(); l1_handler.payload = payload_buffer.span(); @@ -193,37 +193,43 @@ mod Escrow { } #[test] - fn test_fail_random_eth_user_calls_l1_handler() { - let (escrow, _, _) = setup_with_erc20(); - let data: Array = array![1, MM_ETHEREUM().into(), 3, L1_ERC20_ADDRESS().into(), 5]; - let mut payload_buffer: Array = ArrayTrait::new(); - data.serialize(ref payload_buffer); + fn test_fail_claim_erc20_wrong_erc20() { + let (escrow, eth_token, uri_token) = setup_with_erc20(); + + //set order + start_prank(CheatTarget::One(escrow.contract_address), USER()); + let order_erc20 = OrderERC20 { recipient_address: ETH_USER(), amount_l2: 200, amount_l1: 100, fee: 10, l2_erc20_address: uri_token.contract_address, l1_erc20_address: L1_ERC20_ADDRESS() }; + let order_id = escrow.set_order_erc20(order_erc20); + stop_prank(CheatTarget::One(escrow.contract_address)); + + let mut l1_handler = L1HandlerTrait::new( contract_address: escrow.contract_address, - function_name: 'claim_payment_erc20', + function_name: 'claim_payment_erc20' ); - l1_handler.from_address = ETH_USER().into(); + //claim payment + let mut payload_buffer: Array = ArrayTrait::new(); + Serde::serialize(@order_id, ref payload_buffer); + Serde::serialize(@order_erc20.recipient_address, ref payload_buffer); + Serde::serialize(@order_erc20.amount_l2, ref payload_buffer); //wrong, sent amount_l2 instead of amount_l1 + Serde::serialize(@order_erc20.l1_erc20_address, ref payload_buffer); + + l1_handler.from_address = ETH_TRANSFER_CONTRACT().into(); l1_handler.payload = payload_buffer.span(); // same as "Should Panic" but for the L1 handler function match l1_handler.execute() { Result::Ok(_) => panic_with_felt252('shouldve panicked'), Result::Err(RevertedTransaction) => { - assert(*RevertedTransaction.panic_data.at(0) == 'Only PAYMENT_REGISTRY_CONTRACT', *RevertedTransaction.panic_data.at(0)); + assert(*RevertedTransaction.panic_data.at(0) == 'amount_l1 not match L1', *RevertedTransaction.panic_data.at(0)); } } } #[test] - fn test_fail_call_erc20_l1_handler_while_paused() { + fn test_fail_random_eth_user_calls_l1_handler() { let (escrow, _, _) = setup_with_erc20(); - let pausable = IPausableDispatcher { contract_address: escrow.contract_address }; - - start_prank(CheatTarget::One(escrow.contract_address), OWNER()); - escrow.pause(); - stop_prank(CheatTarget::One(escrow.contract_address)); - let data: Array = array![1, MM_ETHEREUM().into(), 3, L1_ERC20_ADDRESS().into(), 5]; let mut payload_buffer: Array = ArrayTrait::new(); data.serialize(ref payload_buffer); @@ -239,7 +245,7 @@ mod Escrow { match l1_handler.execute() { Result::Ok(_) => panic_with_felt252('shouldve panicked'), Result::Err(RevertedTransaction) => { - assert(*RevertedTransaction.panic_data.at(0) == 'Pausable: paused', *RevertedTransaction.panic_data.at(0)); + assert(*RevertedTransaction.panic_data.at(0) == 'Only PAYMENT_REGISTRY_CONTRACT', *RevertedTransaction.panic_data.at(0)); } } } diff --git a/contracts/starknet/src/tests/test_escrow_pause.cairo b/contracts/starknet/src/tests/test_escrow_pause.cairo index b16d7219..cf05ec40 100644 --- a/contracts/starknet/src/tests/test_escrow_pause.cairo +++ b/contracts/starknet/src/tests/test_escrow_pause.cairo @@ -18,7 +18,7 @@ mod Escrow { use yab::tests::utils::{ constants::EscrowConstants::{ - USER, OWNER, MM_STARKNET, MM_ETHEREUM, ETH_TRANSFER_CONTRACT, ETH_USER + USER, OWNER, MM_STARKNET, MM_ETHEREUM, ETH_TRANSFER_CONTRACT, ETH_USER, L1_ERC20_ADDRESS }, }; @@ -268,4 +268,33 @@ mod Escrow { } } } + + #[test] + fn test_fail_call_erc20_l1_handler_while_paused() { + let (escrow, _) = setup(); + let pausable = IPausableDispatcher { contract_address: escrow.contract_address }; + + start_prank(CheatTarget::One(escrow.contract_address), OWNER()); + escrow.pause(); + stop_prank(CheatTarget::One(escrow.contract_address)); + + let data: Array = array![1, MM_ETHEREUM().into(), 3, L1_ERC20_ADDRESS().into(), 5]; + let mut payload_buffer: Array = ArrayTrait::new(); + data.serialize(ref payload_buffer); + let mut l1_handler = L1HandlerTrait::new( + contract_address: escrow.contract_address, + function_name: 'claim_payment_erc20', + ); + l1_handler.from_address = ETH_USER().into(); + + l1_handler.payload = payload_buffer.span(); + + // same as "Should Panic" but for the L1 handler function + match l1_handler.execute() { + Result::Ok(_) => panic_with_felt252('shouldve panicked'), + Result::Err(RevertedTransaction) => { + assert(*RevertedTransaction.panic_data.at(0) == 'Pausable: paused', *RevertedTransaction.panic_data.at(0)); + } + } + } } From 07fe547358973c05358a206f132474eee77973cf Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 18:24:50 -0300 Subject: [PATCH 44/48] fix: repeated test name --- contracts/starknet/src/tests/test_escrow_erc20.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/starknet/src/tests/test_escrow_erc20.cairo b/contracts/starknet/src/tests/test_escrow_erc20.cairo index 9de366e7..1f2a1952 100644 --- a/contracts/starknet/src/tests/test_escrow_erc20.cairo +++ b/contracts/starknet/src/tests/test_escrow_erc20.cairo @@ -193,7 +193,7 @@ mod Escrow { } #[test] - fn test_fail_claim_erc20_wrong_erc20() { + fn test_fail_claim_erc20_wrong_amount_l2() { let (escrow, eth_token, uri_token) = setup_with_erc20(); //set order From f2531df5755113cd1019421410b8835147d1eada Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 18:27:38 -0300 Subject: [PATCH 45/48] test: sn erc20 unit test: wrong recipient address --- .../src/tests/test_escrow_erc20.cairo | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/contracts/starknet/src/tests/test_escrow_erc20.cairo b/contracts/starknet/src/tests/test_escrow_erc20.cairo index 1f2a1952..b3e03a62 100644 --- a/contracts/starknet/src/tests/test_escrow_erc20.cairo +++ b/contracts/starknet/src/tests/test_escrow_erc20.cairo @@ -227,6 +227,41 @@ mod Escrow { } } + #[test] + fn test_fail_claim_erc20_wrong_recipient_address() { + let (escrow, eth_token, uri_token) = setup_with_erc20(); + + //set order + start_prank(CheatTarget::One(escrow.contract_address), USER()); + let order_erc20 = OrderERC20 { recipient_address: ETH_USER(), amount_l2: 200, amount_l1: 100, fee: 10, l2_erc20_address: uri_token.contract_address, l1_erc20_address: L1_ERC20_ADDRESS() }; + let order_id = escrow.set_order_erc20(order_erc20); + stop_prank(CheatTarget::One(escrow.contract_address)); + + + let mut l1_handler = L1HandlerTrait::new( + contract_address: escrow.contract_address, + function_name: 'claim_payment_erc20' + ); + + //claim payment + let mut payload_buffer: Array = ArrayTrait::new(); + Serde::serialize(@order_id, ref payload_buffer); + Serde::serialize(@ETH_USER_2(), ref payload_buffer); //wrong recipient address + Serde::serialize(@order_erc20.amount_l1, ref payload_buffer); + Serde::serialize(@order_erc20.l1_erc20_address, ref payload_buffer); + + l1_handler.from_address = ETH_TRANSFER_CONTRACT().into(); + l1_handler.payload = payload_buffer.span(); + + // same as "Should Panic" but for the L1 handler function + match l1_handler.execute() { + Result::Ok(_) => panic_with_felt252('shouldve panicked'), + Result::Err(RevertedTransaction) => { + assert(*RevertedTransaction.panic_data.at(0) == 'recipient_address not match L1', *RevertedTransaction.panic_data.at(0)); + } + } + } + #[test] fn test_fail_random_eth_user_calls_l1_handler() { let (escrow, _, _) = setup_with_erc20(); From 0546c8f6d6ed532e86fd4add0f90436a822c1d81 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 18:29:56 -0300 Subject: [PATCH 46/48] fix: test_fail_claim_erc20_wrong_erc20 unit test --- contracts/starknet/src/tests/test_escrow_erc20.cairo | 2 +- contracts/starknet/src/tests/utils/constants.cairo | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/contracts/starknet/src/tests/test_escrow_erc20.cairo b/contracts/starknet/src/tests/test_escrow_erc20.cairo index b3e03a62..bdb082e6 100644 --- a/contracts/starknet/src/tests/test_escrow_erc20.cairo +++ b/contracts/starknet/src/tests/test_escrow_erc20.cairo @@ -178,7 +178,7 @@ mod Escrow { Serde::serialize(@order_id, ref payload_buffer); Serde::serialize(@order_erc20.recipient_address, ref payload_buffer); Serde::serialize(@order_erc20.amount_l1, ref payload_buffer); - Serde::serialize(@order_erc20.l2_erc20_address, ref payload_buffer); //wrong, sent to l2_erc20_address instead of l1_erc20_address + Serde::serialize(@L1_ERC20_ADDRESS_2(), ref payload_buffer); //wrong, sent to l2_erc20_address instead of l1_erc20_address l1_handler.from_address = ETH_TRANSFER_CONTRACT().into(); l1_handler.payload = payload_buffer.span(); diff --git a/contracts/starknet/src/tests/utils/constants.cairo b/contracts/starknet/src/tests/utils/constants.cairo index 1a304e82..c354c310 100644 --- a/contracts/starknet/src/tests/utils/constants.cairo +++ b/contracts/starknet/src/tests/utils/constants.cairo @@ -36,4 +36,8 @@ mod EscrowConstants { fn L1_ERC20_ADDRESS() -> EthAddress { 111.try_into().unwrap() } + + fn L1_ERC20_ADDRESS_2() -> EthAddress { + 112.try_into().unwrap() + } } From 7717374d2a98cc53aebd38aa85f222ef21a4c011 Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 18:31:38 -0300 Subject: [PATCH 47/48] fix: import L1_ERC20_ADDRESS_2 var --- contracts/starknet/src/tests/test_escrow_erc20.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/starknet/src/tests/test_escrow_erc20.cairo b/contracts/starknet/src/tests/test_escrow_erc20.cairo index bdb082e6..ff1410b2 100644 --- a/contracts/starknet/src/tests/test_escrow_erc20.cairo +++ b/contracts/starknet/src/tests/test_escrow_erc20.cairo @@ -19,7 +19,7 @@ mod Escrow { use yab::tests::utils::{ constants::EscrowConstants::{ - USER, OWNER, MM_STARKNET, MM_ETHEREUM, ETH_TRANSFER_CONTRACT, ETH_USER, ETH_USER_2, ETH_USER_3, L1_ERC20_ADDRESS + USER, OWNER, MM_STARKNET, MM_ETHEREUM, ETH_TRANSFER_CONTRACT, ETH_USER, ETH_USER_2, ETH_USER_3, L1_ERC20_ADDRESS, L1_ERC20_ADDRESS_2 }, }; From b262ca69942b0afe6a583db7b3213d1ae4ad8d3e Mon Sep 17 00:00:00 2001 From: Urix <43704209+uri-99@users.noreply.github.com> Date: Fri, 19 Apr 2024 18:35:26 -0300 Subject: [PATCH 48/48] refactor: add ModifiedStarknetClaimPaymentERC20Selector event in PaymentRegistry --- contracts/ethereum/src/ERC20_L1.sol | 4 ++-- contracts/ethereum/src/PaymentRegistry.sol | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/contracts/ethereum/src/ERC20_L1.sol b/contracts/ethereum/src/ERC20_L1.sol index 532eb593..a0673684 100644 --- a/contracts/ethereum/src/ERC20_L1.sol +++ b/contracts/ethereum/src/ERC20_L1.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.0; -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; //using upgradeable bc it is the one I have downloaded in my OZ lib +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract UriCoin is ERC20 { constructor() ERC20("UriCoin", "Uri") { - _mint(0xda963fA72caC2A3aC01c642062fba3C099993D56, 1000000); //hardcoded recipient + initial_supply + _mint(0xda963fA72caC2A3aC01c642062fba3C099993D56, 1000000); //TODO unhardcode recipient + initial_supply } } diff --git a/contracts/ethereum/src/PaymentRegistry.sol b/contracts/ethereum/src/PaymentRegistry.sol index cbae7fca..718456b0 100644 --- a/contracts/ethereum/src/PaymentRegistry.sol +++ b/contracts/ethereum/src/PaymentRegistry.sol @@ -22,8 +22,11 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { event ModifiedZKSyncEscrowAddress(address newEscrowAddress); event ModifiedStarknetEscrowAddress(uint256 newEscrowAddress); + event ModifiedStarknetClaimPaymentSelector(uint256 newEscrowClaimPaymentSelector); event ModifiedStarknetClaimPaymentBatchSelector(uint256 newEscrowClaimPaymentBatchSelector); + event ModifiedStarknetClaimPaymentERC20Selector(uint256 newEscrowClaimPaymentERC20Selector); + event ModifiedZKSyncClaimPaymentSelector(bytes4 newZKSyncEscrowClaimPaymentSelector); event ModifiedZKSyncClaimPaymentBatchSelector(bytes4 newZKSyncEscrowClaimPaymentBatchSelector); event ModifiedZKSyncClaimPaymentERC20Selector(bytes4 newZKSyncEscrowClaimPaymentERC20Selector); @@ -341,6 +344,11 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { emit ModifiedStarknetClaimPaymentBatchSelector(StarknetEscrowClaimPaymentBatchSelector); } + function setStarknetClaimPaymentERC20Selector(uint256 NewStarknetEscrowClaimPaymentERC20Selector) external onlyOwner { + StarknetEscrowClaimPaymentERC20Selector = NewStarknetEscrowClaimPaymentERC20Selector; + emit ModifiedStarknetClaimPaymentERC20Selector(StarknetEscrowClaimPaymentERC20Selector); + } + function setZKSyncEscrowClaimPaymentSelector(bytes4 NewZKSyncEscrowClaimPaymentSelector) external onlyOwner { ZKSyncEscrowClaimPaymentSelector = NewZKSyncEscrowClaimPaymentSelector; emit ModifiedZKSyncClaimPaymentSelector(ZKSyncEscrowClaimPaymentSelector); @@ -354,7 +362,6 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable { function setZKSyncEscrowClaimPaymentERC20Selector(bytes4 NewZKSyncEscrowClaimPaymentERC20Selector) external onlyOwner { ZKSyncEscrowClaimPaymentERC20Selector = NewZKSyncEscrowClaimPaymentERC20Selector; emit ModifiedZKSyncClaimPaymentERC20Selector(ZKSyncEscrowClaimPaymentERC20Selector); - }