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 (starknet): add batching functionality for claim_payment #184

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
11 changes: 9 additions & 2 deletions contracts/ethereum/script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,20 @@ contract Deploy is Script {
address snMessagingAddress = vm.envAddress("STARKNET_MESSAGING_ADDRESS");
uint256 snEscrowAddress = 0x0; // this value is set in a call to the smart contract, once deployed
uint256 snClaimPaymentSelector = 0x0; // this value is set in a call to the smart contract, once deployed
uint256 snClaimPaymentBatchSelector = 0x0; // this value is set in a call to the smart contract, once deployed
JuArce marked this conversation as resolved.
Show resolved Hide resolved
address marketMaker = vm.envAddress("MM_ETHEREUM_WALLET_ADDRESS");
address ZKSYNC_DIAMOND_PROXY_ADDRESS = vm.envAddress("ZKSYNC_DIAMOND_PROXY_ADDRESS");

PaymentRegistry yab = new PaymentRegistry();
ERC1967Proxy proxy = new ERC1967Proxy(address(yab), "");
PaymentRegistry(address(proxy)).initialize(snMessagingAddress, snEscrowAddress, snClaimPaymentSelector, marketMaker, ZKSYNC_DIAMOND_PROXY_ADDRESS);

PaymentRegistry(address(proxy)).initialize(
snMessagingAddress,
snEscrowAddress,
snClaimPaymentSelector,
snClaimPaymentBatchSelector,
marketMaker,
ZKSYNC_DIAMOND_PROXY_ADDRESS
);
vm.stopBroadcast();

return (address(proxy), address(yab));
Expand Down
12 changes: 12 additions & 0 deletions contracts/ethereum/set_starknet_claim_payment_selector.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ if [ -z "$CLAIM_PAYMENT_NAME" ]; then
echo "CLAIM_PAYMENT_NAME Variable is empty. Aborting execution.\n"
exit 1
fi
if [ -z "$CLAIM_PAYMENT_BATCH_NAME" ]; then
printf "\n${RED}ERROR:${COLOR_RESET}\n"
echo "CLAIM_PAYMENT_BATCH_NAME Variable is empty. Aborting execution.\n"
exit 1
fi

printf "${GREEN}\n=> [ETH] Setting Starknet ClaimPayment Selector on ETH Smart Contract${COLOR_RESET}\n"
echo "Smart contract being modified:" $PAYMENT_REGISTRY_PROXY_ADDRESS
Expand All @@ -20,3 +25,10 @@ echo "New ClaimPayment Selector: ${CLAIM_PAYMENT_SELECTOR}"

cast send --rpc-url $ETHEREUM_RPC --private-key $ETHEREUM_PRIVATE_KEY $PAYMENT_REGISTRY_PROXY_ADDRESS "setStarknetClaimPaymentSelector(uint256)" "${CLAIM_PAYMENT_SELECTOR}" | grep "transactionHash"
echo "Done setting ClaimPayment selector"

printf "${GREEN}\n=> [ETH] Setting Starknet ClaimPaymentBatch Selector on ETH Smart Contract${COLOR_RESET}\n"
CLAIM_PAYMENT_BATCH_SELECTOR=$(starkli selector $CLAIM_PAYMENT_BATCH_NAME)
echo "New ClaimPaymentBatch Selector: ${CLAIM_PAYMENT_BATCH_SELECTOR}"

cast send --rpc-url $ETHEREUM_RPC --private-key $ETHEREUM_PRIVATE_KEY $PAYMENT_REGISTRY_PROXY_ADDRESS "setStarknetClaimPaymentBatchSelector(uint256)" "${CLAIM_PAYMENT_BATCH_SELECTOR}" | grep "transactionHash"
echo "Done setting ClaimPaymentBatch selector"
67 changes: 59 additions & 8 deletions contracts/ethereum/src/PaymentRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,17 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable {
event ModifiedZKSyncEscrowAddress(address newEscrowAddress);
event ModifiedStarknetEscrowAddress(uint256 newEscrowAddress);
event ModifiedStarknetClaimPaymentSelector(uint256 newEscrowClaimPaymentSelector);
event ClaimPayment(TransferInfo transferInfo);
taturosati marked this conversation as resolved.
Show resolved Hide resolved
event ModifiedStarknetClaimPaymentBatchSelector(uint256 newEscrowClaimPaymentSelector);
event ClaimPayment(uint256 orderId, uint256 destAddress, uint256 amount, Chain chainId);
event ClaimPaymentBatch(uint256[] orderIds, uint256[] destAddresses, uint256[] amounts, Chain chainId);

mapping(bytes32 => TransferInfo) public transfers;
address public marketMaker;
uint256 public StarknetEscrowAddress;
address public ZKSyncEscrowAddress;
uint256 public StarknetEscrowClaimPaymentSelector;
uint256 public StarknetEscrowClaimPaymentBatchSelector;

IZkSync private _ZKSyncDiamondProxy;
IStarknetMessaging private _snMessaging;

Expand All @@ -41,6 +45,7 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable {
address snMessaging,
uint256 StarknetEscrowAddress_,
uint256 StarknetEscrowClaimPaymentSelector_,
uint256 StarknetEscrowClaimPaymentBatchSelector_,
address marketMaker_,
address ZKSyncDiamondProxyAddress) public initializer {
__Ownable_init(msg.sender);
Expand All @@ -51,6 +56,8 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable {

StarknetEscrowAddress = StarknetEscrowAddress_;
StarknetEscrowClaimPaymentSelector = StarknetEscrowClaimPaymentSelector_; // TODO remove this or set the correct value in init
StarknetEscrowClaimPaymentBatchSelector = StarknetEscrowClaimPaymentBatchSelector_; // TODO remove this or set the correct value in init

marketMaker = marketMaker_;
}

Expand All @@ -75,14 +82,12 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable {

//TODO change name to claimPaymentStarknet
function claimPayment(uint256 orderId, uint256 destAddress, uint256 amount) external payable onlyOwnerOrMM {
bytes32 index = keccak256(abi.encodePacked(orderId, destAddress, amount, Chain.Starknet));
TransferInfo storage transferInfo = transfers[index];
require(transferInfo.isUsed == true, "Transfer not found.");
_verifyTransferExistsStarknet(orderId, destAddress, amount);

uint256[] memory payload = new uint256[](5); //TODO why array of 256 if then filled with 128?
payload[0] = uint128(orderId); // low
payload[1] = uint128(orderId >> 128); // high
payload[2] = transferInfo.destAddress;
payload[2] = destAddress;
payload[3] = uint128(amount); // low
payload[4] = uint128(amount >> 128); // high

Expand All @@ -91,7 +96,48 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable {
StarknetEscrowClaimPaymentSelector,
payload);

emit ClaimPayment(transferInfo);
emit ClaimPayment(orderId, destAddress, amount, Chain.Starknet);
}

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

uint256[] memory payload = new uint256[](5 * orderIds.length + 1);

payload[0] = orderIds.length;

for (uint32 idx = 0; idx < orderIds.length; idx++) {
uint256 orderId = orderIds[idx];
uint256 destAddress = destAddresses[idx];
uint256 amount = amounts[idx];

_verifyTransferExistsStarknet(orderId, destAddress, amount);

uint32 base_idx = 1 + 5 * idx;
payload[base_idx] = uint128(orderId); // low
payload[base_idx + 1] = uint128(orderId >> 128); // high
payload[base_idx + 2] = destAddress;
payload[base_idx + 3] = uint128(amount); // low
payload[base_idx + 4] = uint128(amount >> 128); // high
}

_snMessaging.sendMessageToL2{value: msg.value}(
StarknetEscrowAddress,
StarknetEscrowClaimPaymentBatchSelector,
payload);

emit ClaimPaymentBatch(orderIds, destAddresses, amounts, Chain.Starknet);
}

function _verifyTransferExistsStarknet(uint256 orderId, uint256 destAddress, uint256 amount) internal view {
bytes32 index = keccak256(abi.encodePacked(orderId, destAddress, amount, Chain.Starknet));
TransferInfo storage transferInfo = transfers[index];
require(transferInfo.isUsed == true, "Transfer not found.");
}

function claimPaymentZKSync(
Expand Down Expand Up @@ -122,14 +168,15 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable {
msg.sender //refund recipient
);

emit ClaimPayment(transferInfo);
emit ClaimPayment(orderId, destAddress, amount, Chain.ZKSync);
}

function setStarknetEscrowAddress(uint256 newStarknetEscrowAddress) external onlyOwner {
StarknetEscrowAddress = newStarknetEscrowAddress;
emit ModifiedStarknetEscrowAddress(newStarknetEscrowAddress);
}


function setZKSyncEscrowAddress(address newZKSyncEscrowAddress) external onlyOwner {
ZKSyncEscrowAddress = newZKSyncEscrowAddress;
emit ModifiedZKSyncEscrowAddress(newZKSyncEscrowAddress);
Expand All @@ -141,7 +188,11 @@ contract PaymentRegistry is Initializable, OwnableUpgradeable, UUPSUpgradeable {
StarknetEscrowClaimPaymentSelector = NewStarknetEscrowClaimPaymentSelector;
emit ModifiedStarknetClaimPaymentSelector(StarknetEscrowClaimPaymentSelector);
}


function setStarknetClaimPaymentBatchSelector(uint256 NewStarknetEscrowClaimPaymentBatchSelector) external onlyOwner {
StarknetEscrowClaimPaymentBatchSelector = NewStarknetEscrowClaimPaymentBatchSelector;
emit ModifiedStarknetClaimPaymentBatchSelector(StarknetEscrowClaimPaymentBatchSelector);
}

//// MM ACL:

Expand Down
2 changes: 1 addition & 1 deletion contracts/ethereum/test/ACL.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ contract TransferTest is Test {
yab = new PaymentRegistry();
proxy = new ERC1967Proxy(address(yab), "");
yab_caller = PaymentRegistry(address(proxy));
yab_caller.initialize(SN_MESSAGING_ADDRESS, snEscrowAddress, SN_ESCROW_CLAIM_PAYMENT_SELECTOR, marketMaker, ZKSYNC_DIAMOND_PROXY_ADDRESS);
yab_caller.initialize(SN_MESSAGING_ADDRESS, snEscrowAddress, SN_ESCROW_CLAIM_PAYMENT_SELECTOR, 0x0, marketMaker, ZKSYNC_DIAMOND_PROXY_ADDRESS);
taturosati marked this conversation as resolved.
Show resolved Hide resolved

vm.stopPrank();
}
Expand Down
111 changes: 110 additions & 1 deletion contracts/ethereum/test/Transfer_Claim_SN.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "../src/PaymentRegistry.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract TransferTest is Test {
event ClaimPaymentBatch(uint256[] orderIds, uint256[] destAddresses, uint256[] amounts, PaymentRegistry.Chain chainId);

address public deployer = makeAddr('deployer');
address public marketMaker = makeAddr("marketMaker");
Expand All @@ -25,7 +26,7 @@ contract TransferTest is Test {
yab = new PaymentRegistry();
proxy = new ERC1967Proxy(address(yab), "");
yab_caller = PaymentRegistry(address(proxy));
yab_caller.initialize(STARKNET_MESSAGING_ADDRESS, snEscrowAddress, SN_ESCROW_CLAIM_PAYMENT_SELECTOR, marketMaker, ZKSYNC_DIAMOND_PROXY_ADDRESS);
yab_caller.initialize(STARKNET_MESSAGING_ADDRESS, snEscrowAddress, SN_ESCROW_CLAIM_PAYMENT_SELECTOR, 0x0, marketMaker, ZKSYNC_DIAMOND_PROXY_ADDRESS);
taturosati marked this conversation as resolved.
Show resolved Hide resolved

// Mock calls to Starknet Messaging contract
vm.mockCall(
Expand Down Expand Up @@ -82,6 +83,114 @@ contract TransferTest is Test {
yab_caller.claimPayment(1, 0x1, 1);
}

function testClaimPaymentBatch() public {
taturosati marked this conversation as resolved.
Show resolved Hide resolved
hoax(marketMaker, 3 wei);
yab_caller.transfer{value: 3}(1, 0x1, PaymentRegistry.Chain.Starknet);
hoax(marketMaker, 2 wei);
yab_caller.transfer{value: 2}(2, 0x3, PaymentRegistry.Chain.Starknet);
hoax(marketMaker, 1 wei);
yab_caller.transfer{value: 1}(3, 0x5, PaymentRegistry.Chain.Starknet);

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

orderIds[0] = 1;
orderIds[1] = 2;
orderIds[2] = 3;

destAddresses[0] = 0x1;
destAddresses[1] = 0x3;
destAddresses[2] = 0x5;

amounts[0] = 3;
amounts[1] = 2;
amounts[2] = 1;

hoax(marketMaker);
vm.expectEmit(true, true, true, true);
emit ClaimPaymentBatch(orderIds, destAddresses, amounts, PaymentRegistry.Chain.Starknet);
yab_caller.claimPaymentBatch(orderIds, destAddresses, amounts);

assertEq(address(0x1).balance, 3);
assertEq(address(0x3).balance, 2);
assertEq(address(0x5).balance, 1);
}

function testClaimPaymentBatchPartial() public {
hoax(marketMaker, 3 wei);
yab_caller.transfer{value: 3}(1, 0x1, PaymentRegistry.Chain.Starknet);
hoax(marketMaker, 2 wei);
yab_caller.transfer{value: 2}(2, 0x3, PaymentRegistry.Chain.Starknet);
hoax(marketMaker, 1 wei);
yab_caller.transfer{value: 1}(3, 0x5, PaymentRegistry.Chain.Starknet);

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

orderIds[0] = 1;
orderIds[1] = 2;

destAddresses[0] = 0x1;
destAddresses[1] = 0x3;

amounts[0] = 3;
amounts[1] = 2;

hoax(marketMaker);
yab_caller.claimPaymentBatch(orderIds, destAddresses, amounts);

assertEq(address(0x1).balance, 3);
assertEq(address(0x3).balance, 2);
}

function testClaimPaymentBatch_fail_MissingTransfer() public {
hoax(marketMaker, 3 wei);
yab_caller.transfer{value: 3}(1, 0x1, PaymentRegistry.Chain.Starknet);
hoax(marketMaker, 2 wei);
yab_caller.transfer{value: 2}(2, 0x3, PaymentRegistry.Chain.Starknet);

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

orderIds[0] = 1;
orderIds[1] = 2;
orderIds[2] = 3;

destAddresses[0] = 0x1;
destAddresses[1] = 0x3;
destAddresses[2] = 0x5;

amounts[0] = 3;
amounts[1] = 2;
amounts[2] = 1;

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

function testClaimPaymentBatch_fail_notOwnerOrMM() public {
hoax(marketMaker, 3 wei);
yab_caller.transfer{value: 3}(1, 0x1, PaymentRegistry.Chain.Starknet);

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

orderIds[0] = 1;

destAddresses[0] = 0x1;

amounts[0] = 3;

hoax(makeAddr("bob"), 100 wei);
vm.expectRevert("Only Owner or MM can call this function");
yab_caller.claimPaymentBatch(orderIds, destAddresses, amounts);
}

function test_claimPayment_fail_wrongChain() public {
hoax(marketMaker, 1 wei);
yab_caller.transfer{value: 1}(1, 0x1, PaymentRegistry.Chain.Starknet);
Expand Down
2 changes: 1 addition & 1 deletion contracts/ethereum/test/Transfer_Claim_ZKSync.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ contract TransferTest is Test {
yab = new PaymentRegistry();
proxy = new ERC1967Proxy(address(yab), "");
yab_caller = PaymentRegistry(address(proxy));
yab_caller.initialize(SN_MESSAGING_ADDRESS, snEscrowAddress, SN_ESCROW_CLAIM_PAYMENT_SELECTOR, marketMaker, ZKSYNC_DIAMOND_PROXY_ADDRESS);
yab_caller.initialize(SN_MESSAGING_ADDRESS, snEscrowAddress, SN_ESCROW_CLAIM_PAYMENT_SELECTOR, 0x0, marketMaker, ZKSYNC_DIAMOND_PROXY_ADDRESS);

//Mock calls to ZKSync Mailbox contract
vm.mockCall(
Expand Down
1 change: 1 addition & 0 deletions contracts/starknet/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ STARKNET_RPC=<starknet_rpc_url>
STARKNET_ESCROW_OWNER=<starknet_escrow_owner> #in lowercase hexa with the 0x prefix
MM_STARKNET_WALLET_ADDRESS=<MarketMaker_starknet_address> #in lowercase hexa with the 0x prefix
CLAIM_PAYMENT_NAME=<claim_payment_function_name> #must match the exact name of the function to claim the payment from the starknet smart contract
CLAIM_PAYMENT_BATCH_NAME=<claim_payment_batch_function_name> #must match the exact name of the function to claim the payment batch from the starknet smart contract
taturosati marked this conversation as resolved.
Show resolved Hide resolved
MM_ETHEREUM_WALLET_ADDRESS=<MarketMaker_ethereum_contract_address> #in lowercase hexa with the 0x prefix
NATIVE_TOKEN_ETH_STARKNET=<eth_erc20_in_starknet> #in lowercase hexa with the 0x prefix
1 change: 1 addition & 0 deletions contracts/starknet/.env.test
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ STARKNET_RPC=http://0.0.0.0:5050
STARKNET_ESCROW_OWNER=0x517ececd29116499f4a1b64b094da79ba08dfd54a3edaa316134c41f8160973
MM_STARKNET_WALLET_ADDRESS=0x5686a647a9cdd63ade617e0baf3b364856b813b508f03903eb58a7e622d5855
CLAIM_PAYMENT_NAME=claim_payment
CLAIM_PAYMENT_BATCH_NAME=claim_payment_batch
MM_ETHEREUM_WALLET_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
NATIVE_TOKEN_ETH_STARKNET=0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7
Loading
Loading