Skip to content

Commit 2f7cc0a

Browse files
feat(zkgm): faster than finality market making (#4363)
- allow order filling before finality - disallow forwarding filling before finality - update eureka module entrypoint to reflect the new operation `OnZkgm | OnIntentZkgm` - thread more informations to eureka modules
2 parents fed19d2 + 915ed70 commit 2f7cc0a

File tree

10 files changed

+1059
-491
lines changed

10 files changed

+1059
-491
lines changed

cosmwasm/ibc-union/app/ucs03-zkgm/src/contract.rs

+230-107
Large diffs are not rendered by default.

cosmwasm/ibc-union/app/ucs03-zkgm/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,6 @@ pub enum ContractError {
123123
OnlyRateLimitAdmin,
124124
#[error("invalid operation, sender must be a rate limit operator")]
125125
OnlyRateLimitOperator,
126+
#[error("the instruction cannot be executed by a market maker")]
127+
InvalidMarketMakerOperation,
126128
}

cosmwasm/ibc-union/app/ucs03-zkgm/src/msg.rs

+14
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ pub enum ExecuteMsg {
7979
packet: Packet,
8080
relayer: Addr,
8181
relayer_msg: Bytes,
82+
intent: bool,
8283
},
8384
/// Write an acknowledgement for an Zkgm packet.
8485
/// Can only be called by the contract itself after packet execution.
@@ -94,11 +95,24 @@ pub enum ExecuteMsg {
9495
#[serde(deny_unknown_fields, rename_all = "snake_case")]
9596
pub enum ZkgmMsg {
9697
OnZkgm {
98+
caller: Addr,
99+
path: Uint256,
100+
source_channel_id: ChannelId,
101+
destination_channel_id: ChannelId,
102+
sender: Bytes,
103+
message: Bytes,
104+
relayer: Addr,
105+
relayer_msg: Bytes,
106+
},
107+
OnIntentZkgm {
108+
caller: Addr,
97109
path: Uint256,
98110
source_channel_id: ChannelId,
99111
destination_channel_id: ChannelId,
100112
sender: Bytes,
101113
message: Bytes,
114+
market_maker: Addr,
115+
market_maker_msg: Bytes,
102116
},
103117
}
104118

cosmwasm/ibc-union/app/ucs03-zkgm/src/tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,7 @@ fn test_execute_internal_execute_only_self() {
620620
},
621621
relayer: Addr::unchecked(""),
622622
relayer_msg: Default::default(),
623+
intent: false,
623624
},
624625
);
625626
assert_eq!(result, Err(ContractError::OnlySelf));

evm/contracts/apps/ucs/00-pingpong/PingPong.sol

+18
Original file line numberDiff line numberDiff line change
@@ -236,4 +236,22 @@ contract PingPong is
236236
path, sourceChannelId, destinationChannelId, sender, message
237237
);
238238
}
239+
240+
function onIntentZkgm(
241+
address caller,
242+
uint256 path,
243+
uint32 sourceChannelId,
244+
uint32 destinationChannelId,
245+
bytes calldata sender,
246+
bytes calldata message,
247+
address relayer,
248+
bytes calldata relayerMsg
249+
) public {
250+
if (msg.sender != zkgmProtocol) {
251+
revert PingPongLib.ErrOnlyZKGM();
252+
}
253+
emit PingPongLib.Zkgoblim(
254+
path, sourceChannelId, destinationChannelId, sender, message
255+
);
256+
}
239257
}

evm/contracts/apps/ucs/03-zkgm/IZkgmable.sol

+11
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,15 @@ interface IZkgmable {
1111
address relayer,
1212
bytes calldata relayerMsg
1313
) external;
14+
15+
function onIntentZkgm(
16+
address caller,
17+
uint256 path,
18+
uint32 sourceChannelId,
19+
uint32 destinationChannelId,
20+
bytes calldata sender,
21+
bytes calldata message,
22+
address marketMaker,
23+
bytes calldata marketMakerMsg
24+
) external;
1425
}

evm/contracts/apps/ucs/03-zkgm/Lib.sol

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ library ZkgmLib {
6060
error ErrInvalidForwardInstruction();
6161
error ErrInvalidMultiplexSender();
6262
error ErrInvalidForwardDestinationChannelId();
63+
error ErrInvalidMarketMakerOperation();
6364

6465
function encodeFungibleAssetOrderAck(
6566
FungibleAssetOrderAck memory ack

evm/contracts/apps/ucs/03-zkgm/Zkgm.sol

+106-27
Original file line numberDiff line numberDiff line change
@@ -279,14 +279,35 @@ contract UCS03Zkgm is
279279
}
280280
}
281281

282+
function onRecvIntentPacket(
283+
address caller,
284+
IBCPacket calldata packet,
285+
address relayer,
286+
bytes calldata relayerMsg
287+
) external virtual override onlyIBC whenNotPaused returns (bytes memory) {
288+
return _processReceive(caller, packet, relayer, relayerMsg, true);
289+
}
290+
282291
function onRecvPacket(
283292
address caller,
284293
IBCPacket calldata packet,
285294
address relayer,
286295
bytes calldata relayerMsg
287296
) external virtual override onlyIBC whenNotPaused returns (bytes memory) {
297+
return _processReceive(caller, packet, relayer, relayerMsg, false);
298+
}
299+
300+
function _processReceive(
301+
address caller,
302+
IBCPacket calldata packet,
303+
address relayer,
304+
bytes calldata relayerMsg,
305+
bool intent
306+
) internal returns (bytes memory) {
288307
(bool success, bytes memory returnData) = address(this).call(
289-
abi.encodeCall(this.execute, (caller, packet, relayer, relayerMsg))
308+
abi.encodeCall(
309+
this.execute, (caller, packet, relayer, relayerMsg, intent)
310+
)
290311
);
291312
// Avoid gas-starvation trick. Enforce a minimum for griefing relayers.
292313
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/bd325d56b4c62c9c5c1aff048c37c6bb18ac0290/contracts/metatx/MinimalForwarder.sol#L58-L68
@@ -325,7 +346,8 @@ contract UCS03Zkgm is
325346
address caller,
326347
IBCPacket calldata ibcPacket,
327348
address relayer,
328-
bytes calldata relayerMsg
349+
bytes calldata relayerMsg,
350+
bool intent
329351
) public returns (bytes memory) {
330352
// Only callable through the onRecvPacket endpoint.
331353
if (msg.sender != address(this)) {
@@ -339,7 +361,8 @@ contract UCS03Zkgm is
339361
relayerMsg,
340362
zkgmPacket.salt,
341363
zkgmPacket.path,
342-
zkgmPacket.instruction
364+
zkgmPacket.instruction,
365+
intent
343366
);
344367
}
345368

@@ -350,7 +373,8 @@ contract UCS03Zkgm is
350373
bytes calldata relayerMsg,
351374
bytes32 salt,
352375
uint256 path,
353-
Instruction calldata instruction
376+
Instruction calldata instruction,
377+
bool intent
354378
) internal returns (bytes memory) {
355379
if (instruction.opcode == ZkgmLib.OP_FUNGIBLE_ASSET_ORDER) {
356380
if (instruction.version != ZkgmLib.INSTR_VERSION_1) {
@@ -359,7 +383,7 @@ contract UCS03Zkgm is
359383
FungibleAssetOrder calldata order =
360384
ZkgmLib.decodeFungibleAssetOrder(instruction.operand);
361385
return _executeFungibleAssetOrder(
362-
caller, ibcPacket, relayer, relayerMsg, path, order
386+
caller, ibcPacket, relayer, relayerMsg, path, order, intent
363387
);
364388
} else if (instruction.opcode == ZkgmLib.OP_BATCH) {
365389
if (instruction.version > ZkgmLib.INSTR_VERSION_0) {
@@ -372,7 +396,8 @@ contract UCS03Zkgm is
372396
relayerMsg,
373397
salt,
374398
path,
375-
ZkgmLib.decodeBatch(instruction.operand)
399+
ZkgmLib.decodeBatch(instruction.operand),
400+
intent
376401
);
377402
} else if (instruction.opcode == ZkgmLib.OP_FORWARD) {
378403
if (instruction.version > ZkgmLib.INSTR_VERSION_0) {
@@ -385,7 +410,8 @@ contract UCS03Zkgm is
385410
salt,
386411
path,
387412
instruction.version,
388-
ZkgmLib.decodeForward(instruction.operand)
413+
ZkgmLib.decodeForward(instruction.operand),
414+
intent
389415
);
390416
} else if (instruction.opcode == ZkgmLib.OP_MULTIPLEX) {
391417
if (instruction.version > ZkgmLib.INSTR_VERSION_0) {
@@ -398,7 +424,8 @@ contract UCS03Zkgm is
398424
relayerMsg,
399425
path,
400426
salt,
401-
ZkgmLib.decodeMultiplex(instruction.operand)
427+
ZkgmLib.decodeMultiplex(instruction.operand),
428+
intent
402429
);
403430
} else {
404431
revert ZkgmLib.ErrUnknownOpcode();
@@ -412,7 +439,8 @@ contract UCS03Zkgm is
412439
bytes calldata relayerMsg,
413440
bytes32 salt,
414441
uint256 path,
415-
Batch calldata batch
442+
Batch calldata batch,
443+
bool intent
416444
) internal returns (bytes memory) {
417445
uint256 l = batch.instructions.length;
418446
bytes[] memory acks = new bytes[](l);
@@ -425,7 +453,8 @@ contract UCS03Zkgm is
425453
relayerMsg,
426454
ZkgmLib.deriveBatchSalt(i, salt),
427455
path,
428-
instruction
456+
instruction,
457+
intent
429458
);
430459
// We should have the guarantee that the acks are non empty because
431460
// the only instructions allowed in a batch are multiplex and
@@ -448,8 +477,19 @@ contract UCS03Zkgm is
448477
bytes32 salt,
449478
uint256 path,
450479
uint8 version,
451-
Forward calldata forward
480+
Forward calldata forward,
481+
bool intent
452482
) internal returns (bytes memory) {
483+
// We cannot allow market makers to fill packets containing forward
484+
// instruction. This would allow them to submit of a proof and fill via the
485+
// protocol on destination for a fake forward.
486+
487+
// Instead, they must first fill on destination the orders, awaits finality
488+
// to settle the forward, then cascade acknowledge.
489+
if (intent) {
490+
revert ZkgmLib.ErrInvalidMarketMakerOperation();
491+
}
492+
453493
(uint256 tailPath, uint32 previousDestinationChannelId) =
454494
ZkgmLib.dequeueChannelFromPath(forward.path);
455495
(uint256 continuationPath, uint32 nextSourceChannelId) =
@@ -508,20 +548,34 @@ contract UCS03Zkgm is
508548
bytes calldata relayerMsg,
509549
uint256 path,
510550
bytes32 salt,
511-
Multiplex calldata multiplex
551+
Multiplex calldata multiplex,
552+
bool intent
512553
) internal returns (bytes memory) {
513554
address contractAddress = address(bytes20(multiplex.contractAddress));
514555
if (!multiplex.eureka) {
515-
IZkgmable(contractAddress).onZkgm(
516-
caller,
517-
path,
518-
ibcPacket.sourceChannelId,
519-
ibcPacket.destinationChannelId,
520-
multiplex.sender,
521-
multiplex.contractCalldata,
522-
relayer,
523-
relayerMsg
524-
);
556+
if (intent) {
557+
IZkgmable(contractAddress).onIntentZkgm(
558+
caller,
559+
path,
560+
ibcPacket.sourceChannelId,
561+
ibcPacket.destinationChannelId,
562+
multiplex.sender,
563+
multiplex.contractCalldata,
564+
relayer,
565+
relayerMsg
566+
);
567+
} else {
568+
IZkgmable(contractAddress).onZkgm(
569+
caller,
570+
path,
571+
ibcPacket.sourceChannelId,
572+
ibcPacket.destinationChannelId,
573+
multiplex.sender,
574+
multiplex.contractCalldata,
575+
relayer,
576+
relayerMsg
577+
);
578+
}
525579
return abi.encode(ZkgmLib.ACK_SUCCESS);
526580
} else {
527581
IBCPacket memory multiplexIbcPacket = IBCPacket({
@@ -533,8 +587,17 @@ contract UCS03Zkgm is
533587
timeoutHeight: ibcPacket.timeoutHeight,
534588
timeoutTimestamp: ibcPacket.timeoutTimestamp
535589
});
536-
bytes memory acknowledgement = IIBCModuleRecv(contractAddress)
537-
.onRecvPacket(caller, multiplexIbcPacket, relayer, relayerMsg);
590+
bytes memory acknowledgement;
591+
if (intent) {
592+
acknowledgement = IIBCModuleRecv(contractAddress)
593+
.onRecvIntentPacket(
594+
caller, multiplexIbcPacket, relayer, relayerMsg
595+
);
596+
} else {
597+
acknowledgement = IIBCModuleRecv(contractAddress).onRecvPacket(
598+
caller, multiplexIbcPacket, relayer, relayerMsg
599+
);
600+
}
538601
if (acknowledgement.length == 0) {
539602
revert ZkgmLib.ErrAsyncMultiplexUnsupported();
540603
}
@@ -695,13 +758,26 @@ contract UCS03Zkgm is
695758
address relayer,
696759
bytes calldata relayerMsg,
697760
uint256 path,
698-
FungibleAssetOrder calldata order
761+
FungibleAssetOrder calldata order,
762+
bool intent
699763
) internal returns (bytes memory) {
764+
address quoteToken = address(bytes20(order.quoteToken));
765+
address payable receiver = payable(address(bytes20(order.receiver)));
766+
767+
// For intent packets, the protocol is not allowed to provide any fund
768+
// as the packet has not been checked for membership poof. Instead, we
769+
// know the market maker will be repaid on the source chain, if and only
770+
// if the currently executing packet hash had been registered as sent on
771+
// the source. In other words, the market maker is unable to lie.
772+
if (intent) {
773+
return _marketMakerFill(
774+
caller, relayerMsg, quoteToken, receiver, order.quoteAmount
775+
);
776+
}
777+
700778
(address wrappedToken, bytes32 wrappedTokenSalt) = _predictWrappedToken(
701779
path, ibcPacket.destinationChannelId, order.baseToken
702780
);
703-
address quoteToken = address(bytes20(order.quoteToken));
704-
address payable receiver = payable(address(bytes20(order.receiver)));
705781
bool baseAmountCoversQuoteAmount = order.baseAmount >= order.quoteAmount;
706782
if (quoteToken == wrappedToken && baseAmountCoversQuoteAmount) {
707783
_rateLimit(quoteToken, order.quoteAmount);
@@ -739,6 +815,9 @@ contract UCS03Zkgm is
739815
false
740816
);
741817
} else {
818+
// We also allow market makers to fill orders after finality. This
819+
// allow orders that combines protocol and mm filling (wrapped vs
820+
// non wrapped assets).
742821
return _marketMakerFill(
743822
caller, relayerMsg, quoteToken, receiver, order.quoteAmount
744823
);

evm/contracts/core/05-port/IIBCModule.sol

+7-7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ interface IIBCModuleRecv {
99
address relayer,
1010
bytes calldata relayerMsg
1111
) external returns (bytes memory);
12+
13+
function onRecvIntentPacket(
14+
address caller,
15+
IBCPacket calldata packet,
16+
address marketMaker,
17+
bytes calldata marketMakerMsg
18+
) external returns (bytes memory);
1219
}
1320

1421
// IIBCModule defines an interface that implements all the callbacks
@@ -59,13 +66,6 @@ interface IIBCModule is IIBCModuleRecv {
5966
address relayer
6067
) external;
6168

62-
function onRecvIntentPacket(
63-
address caller,
64-
IBCPacket calldata packet,
65-
address marketMaker,
66-
bytes calldata marketMakerMsg
67-
) external returns (bytes memory);
68-
6969
function onAcknowledgementPacket(
7070
address caller,
7171
IBCPacket calldata packet,

0 commit comments

Comments
 (0)