Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat (zksync): add batching functionality for claim payment #198

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 42 additions & 2 deletions contracts/ethereum/src/PaymentRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,7 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable {
uint256 gasLimit,
uint256 gasPerPubdataByteLimit
) external payable onlyOwnerOrMM {
bytes32 index = keccak256(abi.encodePacked(orderId, destAddress, amount, Chain.ZKSync));
require(transfers[index] == true, "Transfer not found."); //if this is claimed twice, Escrow will know
_verifyTransferExistsZKSync(orderId, destAddress, amount);

//todo change place of this var
bytes4 selector = 0xa5168739; //claim_payment selector in ZKSync //todo add in init, same as in SN
Expand All @@ -159,6 +158,47 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable {
emit ClaimPayment(orderId, destAddress, amount, Chain.ZKSync); //2100 gas
}

function claimPaymentBatchZKSync(
uint256[] calldata orderIds,
address[] calldata destAddresses,
uint256[] calldata amounts,
uint256 gasLimit,
uint256 gasPerPubdataByteLimit
) external payable onlyOwnerOrMM {
require(orderIds.length == destAddresses.length, "Invalid lengths.");
require(orderIds.length == amounts.length, "Invalid lengths.");

for (uint32 idx = 0; idx < orderIds.length; idx++) {
_verifyTransferExistsZKSync(orderIds[idx], destAddresses[idx], amounts[idx]);
}

//todo change place of this var
bytes4 selector = 0x156be1ae; //claim_payment_batch selector in ZKSync //todo add in init, same as in SN
bytes memory messageToL2 = abi.encodeWithSelector(
selector,
orderIds,
destAddresses,
amounts
);

_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 ClaimPaymentBatch(orderIds, destAddresses, amounts, Chain.ZKSync);
}

function _verifyTransferExistsZKSync(uint256 orderId, address destAddress, uint256 amount) internal view {
bytes32 index = keccak256(abi.encodePacked(orderId, destAddress, amount, Chain.ZKSync));
require(transfers[index] == true, "Transfer not found."); //if this is claimed twice, Escrow will know
}

function setStarknetEscrowAddress(uint256 newStarknetEscrowAddress) external onlyOwner {
StarknetEscrowAddress = newStarknetEscrowAddress;
emit ModifiedStarknetEscrowAddress(newStarknetEscrowAddress);
Expand Down
65 changes: 65 additions & 0 deletions contracts/ethereum/test/Transfer_Claim_ZKSync.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,71 @@ contract TransferTest is Test {
assertEq(address(marketMaker).balance, 100);
}

function test_claimPaymentBatch_zk() public {
hoax(marketMaker, 100 wei);
yab_caller.transfer{value: 100}(1, address(0x1), PaymentRegistry.Chain.ZKSync);
hoax(marketMaker, 101 wei);
yab_caller.transfer{value: 100}(2, address(0x1), PaymentRegistry.Chain.ZKSync);

uint256[] memory orderIds = new uint256[](2);
address[] memory destAddresses = new address[](2);
uint256[] memory amounts = new uint256[](2);

orderIds[0] = 1;
orderIds[1] = 2;
destAddresses[0] = address(0x1);
destAddresses[1] = address(0x1);
amounts[0] = 100;
amounts[1] = 100;

vm.mockCall(
ZKSYNC_DIAMOND_PROXY_ADDRESS,
abi.encodeWithSelector(0x156be1ae, 0), //TODO add selector
abi.encode(0x12345678901234567890123456789012) //TODO add return data
);
hoax(marketMaker);
yab_caller.claimPaymentBatchZKSync(orderIds, destAddresses, amounts, 1, 1);
}

function test_claimPaymentBatch_zk_fail_MissingTransfer() public {
hoax(marketMaker, 100 wei);
yab_caller.transfer{value: 100}(1, address(0x1), PaymentRegistry.Chain.ZKSync);

uint256[] memory orderIds = new uint256[](2);
address[] memory destAddresses = new address[](2);
uint256[] memory amounts = new uint256[](2);

orderIds[0] = 1;
orderIds[1] = 2;
destAddresses[0] = address(0x1);
destAddresses[1] = address(0x1);
amounts[0] = 100;
amounts[1] = 100;

vm.expectRevert("Transfer not found.");
hoax(marketMaker);
yab_caller.claimPaymentBatchZKSync(orderIds, destAddresses, amounts, 1, 1);
}

function test_claimPaymentBatch_zk_fail_notOwnerOrMM() public {
hoax(marketMaker, 100 wei);
yab_caller.transfer{value: 100}(1, address(0x1), PaymentRegistry.Chain.ZKSync);

uint256[] memory orderIds = new uint256[](2);
address[] memory destAddresses = new address[](2);
uint256[] memory amounts = new uint256[](2);

orderIds[0] = 1;
orderIds[1] = 2;
destAddresses[0] = address(0x1);
destAddresses[1] = address(0x1);
amounts[0] = 100;
amounts[1] = 100;

vm.expectRevert("Only Owner or MM can call this function");
yab_caller.claimPaymentBatchZKSync(orderIds, destAddresses, amounts, 0, 1);
}

function test_claimPayment_zk_maxInt() public {
uint256 maxInt = type(uint256).max;

Expand Down
32 changes: 32 additions & 0 deletions contracts/zksync/contracts/escrow.sol
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,38 @@ contract Escrow is Initializable, OwnableUpgradeable, PausableUpgradeable { //},
emit ClaimPayment(order_id, mm_zksync_wallet, amount);
}

// l1 handler
function claim_payment_batch(
uint256[] calldata order_ids,
address[] calldata recipient_addresses,
uint256[] calldata amounts
) public whenNotPaused {
require(msg.sender == ethereum_payment_registry, 'Only PAYMENT_REGISTRY can call');
require(order_ids.length == recipient_addresses.length, 'Invalid lengths');
require(order_ids.length == amounts.length, 'Invalid lengths');

for (uint32 idx = 0; idx < order_ids.length; idx++) {
uint256 order_id = order_ids[idx];
address recipient_address = recipient_addresses[idx];
uint256 amount = amounts[idx];

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

// TODO: Might be best to do only one transfer
(bool success,) = payable(address(uint160(mm_zksync_wallet))).call{value: payment_amount}("");
require(success, "Transfer failed.");

emit ClaimPayment(order_id, mm_zksync_wallet, amount);
}
}

function is_order_pending(uint256 order_id) public view returns (bool) {
return _orders_pending[order_id];
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/zksync/deploy/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ export default async function () {

const initResult = await escrow.initialize(PaymentRegistryL2Alias, mm_zksync_wallet);

// console.log("Initialization result:", initResult);
console.log("Initialization result:", initResult);
}
4 changes: 2 additions & 2 deletions contracts/zksync/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import "@matterlabs/hardhat-zksync-upgradable";
import "@nomicfoundation/hardhat-chai-matchers";

const config: HardhatUserConfig = {
defaultNetwork: "dockerizedNode",
defaultNetwork: "inMemoryNode",
// defaultNetwork: "zkSyncSepoliaTestnet",
networks: {
zkSyncSepoliaTestnet: {
Expand All @@ -35,7 +35,7 @@ const config: HardhatUserConfig = {
zksync: true,
},
inMemoryNode: {
url: "http://127.0.0.1:8011",
url: "http://localhost:8011",
ethNetwork: "", // in-memory node doesn't support eth node; removing this line will cause an error
zksync: true,
},
Expand Down
2 changes: 1 addition & 1 deletion contracts/zksync/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,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 inMemoryNode --show-stack-traces"
},
"devDependencies": {
"@matterlabs/hardhat-zksync-deploy": "^1.1.2",
Expand Down
36 changes: 36 additions & 0 deletions contracts/zksync/test/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,39 @@ describe('Ownable tests', function () {
});

})

describe('Claim payment batch tests', function () {

it("Should claim payment batch", async () => {
const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value});
await setOrderTx.wait();

const setOrderTx2 = await escrow.connect(user_zk2).set_order(user_eth2, fee, {value});
await setOrderTx2.wait();

await escrow.connect(deployer).set_ethereum_payment_registry(user_eth);
await escrow.connect(deployer).set_mm_zksync_wallet(user_zk);

const tx = await escrow.connect(user_eth).claim_payment_batch([0, 1], [user_eth.address, user_eth2.address], [value-fee, value-fee]);

await expect(tx)
.to.emit(escrow, "ClaimPayment").withArgs(0, user_zk.address, value-fee)
.to.emit(escrow, "ClaimPayment").withArgs(1, user_zk.address, value-fee);

expect(await escrow.is_order_pending(0)).to.equal(false);
});

it("Should not claim payment batch when order missing", async () => {
await escrow.connect(deployer).set_ethereum_payment_registry(user_eth);

await expect(escrow.connect(user_eth).claim_payment_batch([0], [user_eth.address], [value-fee])).to.be.revertedWith("Order claimed or nonexistent");
});

it("Should not claim payment batch when not PAYMENT_REGISTRY", async () => {
const setOrderTx = await escrow.connect(user_zk).set_order(user_eth, fee, {value});
await setOrderTx.wait();

await expect(escrow.connect(user_eth).claim_payment_batch([0], [user_eth.address], [value-fee])).to.be.revertedWith("Only PAYMENT_REGISTRY can call");
});
});

Loading