Skip to content

Commit

Permalink
feat: do not move funds with call kind
Browse files Browse the repository at this point in the history
  • Loading branch information
karlem committed Nov 27, 2024
1 parent dcf324f commit a7c17ee
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 66 deletions.
25 changes: 11 additions & 14 deletions contracts/contracts/gateway/GatewayMessengerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,16 @@ contract GatewayMessengerFacet is GatewayActorModifiers {
using CrossMsgHelper for IpcEnvelope;

/**
* @dev Sends a general-purpose cross-message from the local subnet to the destination subnet.
* Any value in msg.value will be forwarded in the call.
*
* IMPORTANT: Only smart contracts are allowed to trigger these cross-net messages. User wallets can send funds
* from their address to the destination subnet and then run the transaction in the destination normally.
*
* @param envelope - the original envelope, which will be validated, stamped and committed during the send.
* @return committed envelope.
*/
* @dev Sends a general-purpose cross-message from the local subnet to the destination subnet.
* IMPORTANT: Native tokens via msg.value are treated as a contribution toward gas costs associated with message propagation.
* There is no strict enforcement of the exact gas cost, and any msg.value provided will be accepted.
*
* IMPORTANT: Only smart contracts are allowed to trigger these cross-net messages. User wallets can send funds
* from their address to the destination subnet and then run the transaction in the destination normally.
*
* @param envelope - the original envelope, which will be validated, stamped, and committed during the send.
* @return committed envelope.
*/
function sendContractXnetMessage(
IpcEnvelope memory envelope
) external payable returns (IpcEnvelope memory committed) {
Expand All @@ -46,10 +47,6 @@ contract GatewayMessengerFacet is GatewayActorModifiers {
revert InvalidXnetMessage(InvalidXnetMessageReason.Sender);
}

if (envelope.value != msg.value) {
revert InvalidXnetMessage(InvalidXnetMessageReason.Value);
}

if (envelope.kind != IpcMsgKind.Call) {
revert InvalidXnetMessage(InvalidXnetMessageReason.Kind);
}
Expand All @@ -61,7 +58,7 @@ contract GatewayMessengerFacet is GatewayActorModifiers {
kind: IpcMsgKind.Call,
from: IPCAddress({subnetId: s.networkName, rawAddress: FvmAddressHelper.from(msg.sender)}),
to: envelope.to,
value: msg.value,
value: envelope.value,
message: envelope.message,
nonce: 0 // nonce will be updated by LibGateway.commitCrossMessage
});
Expand Down
8 changes: 5 additions & 3 deletions contracts/contracts/gateway/router/CheckpointingFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {NotRegisteredSubnet, SubnetNotActive, SubnetNotFound, InvalidSubnet, Che
import {BatchNotCreated, InvalidBatchEpoch, BatchAlreadyExists, NotEnoughSubnetCircSupply, InvalidCheckpointEpoch} from "../../errors/IPCErrors.sol";

import {CrossMsgHelper} from "../../lib/CrossMsgHelper.sol";
import {IpcEnvelope, SubnetID} from "../../structs/CrossNet.sol";
import {IpcEnvelope, SubnetID, IpcMsgKind} from "../../structs/CrossNet.sol";
import {SubnetIDHelper} from "../../lib/SubnetIDHelper.sol";

contract CheckpointingFacet is GatewayActorModifiers {
Expand Down Expand Up @@ -127,7 +127,9 @@ contract CheckpointingFacet is GatewayActorModifiers {
uint256 crossMsgLength = msgs.length;

for (uint256 i; i < crossMsgLength; ) {
totalValue += msgs[i].value;
if (msgs[i].kind == IpcMsgKind.Transfer) {
totalValue += msgs[i].value;
}
unchecked {
++i;
}
Expand All @@ -138,7 +140,7 @@ contract CheckpointingFacet is GatewayActorModifiers {
if (subnet.circSupply < totalAmount) {
revert NotEnoughSubnetCircSupply();
}

subnet.circSupply -= totalAmount;

// execute cross-messages
Expand Down
4 changes: 3 additions & 1 deletion contracts/contracts/lib/LibGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,9 @@ library LibGateway {

crossMessage.nonce = topDownNonce;
subnet.topDownNonce = topDownNonce + 1;
subnet.circSupply += crossMessage.value;
if (crossMessage.kind == IpcMsgKind.Transfer) {
subnet.circSupply += crossMessage.value;
}

emit NewTopDownMessage({subnet: subnet.id.getAddress(), message: crossMessage, id: crossMessage.toDeterministicHash()});
}
Expand Down
13 changes: 9 additions & 4 deletions contracts/contracts/structs/CrossNet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,15 @@ struct IpcEnvelope {
/// @dev outgoing nonce for the envelope.
/// This nonce is set by the gateway when committing the message for propagation
uint64 nonce;
/// @dev value being sent in the message.
/// If we want receipts to return value, and all messages to be able
/// to handle different supply sources we can expose the value
/// as a common field.
/// @dev Value being sent in the message.
/// For `Call` and `Result` kinds, the `value` field is synthetic and does not represent an actual token or native currency transfer.
/// Instead, it serves as metadata or an abstract representation of value to be interpreted by the target contract or receipt handler.
///
/// For example, in a `Call` message, `value` might represent the intended payment amount for a service in a cross-network dApp,
/// allowing the receiving contract to process it as part of its logic, regardless of the actual token transfer mechanics.
/// Similarly, in a `Result` message, `value` might represent the outcome of a transaction, such as the total tokens minted or refunded.
///
/// For `Transfer` messages, `value` represents the actual amount of native tokens being transferred across networks.
uint256 value;
/// @dev abi.encoded message
bytes message;
Expand Down
46 changes: 2 additions & 44 deletions contracts/test/integration/GatewayDiamond.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -538,28 +538,6 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT
gatewayDiamond.manager().kill();
}

function testGatewayDiamond_SendCrossMessage_Fails_NoFunds() public {
address caller = address(new MockIpcContract());
vm.startPrank(caller);
vm.deal(caller, DEFAULT_COLLATERAL_AMOUNT + DEFAULT_CROSS_MSG_FEE + 2);
registerSubnet(DEFAULT_COLLATERAL_AMOUNT, caller);

SubnetID memory destinationSubnet = gatewayDiamond.getter().getNetworkName().createSubnetId(caller);

vm.expectRevert(abi.encodeWithSelector(InvalidXnetMessage.selector, InvalidXnetMessageReason.Value));
gatewayDiamond.messenger().sendContractXnetMessage{value: DEFAULT_CROSS_MSG_FEE}(
TestUtils.newXnetCallMsg(
IPCAddress({
subnetId: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}),
rawAddress: FvmAddressHelper.from(caller)
}),
IPCAddress({subnetId: destinationSubnet, rawAddress: FvmAddressHelper.from(caller)}),
1,
0
)
);
}

function testGatewayDiamond_SendCrossMessage_Fails_Fuzz(uint256 fee) public {
vm.assume(fee < DEFAULT_CROSS_MSG_FEE);

Expand Down Expand Up @@ -878,27 +856,6 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT
);
}

function testGatewayDiamond_SendCrossMessage_Fails_Failes_InvalidCrossMsgValue() public {
address caller = address(new MockIpcContract());
vm.startPrank(caller);
vm.deal(caller, DEFAULT_COLLATERAL_AMOUNT + DEFAULT_CROSS_MSG_FEE + 2);
registerSubnet(DEFAULT_COLLATERAL_AMOUNT, caller);
SubnetID memory destinationSubnet = gatewayDiamond.getter().getNetworkName().createSubnetId(caller);

vm.expectRevert(abi.encodeWithSelector(InvalidXnetMessage.selector, InvalidXnetMessageReason.Value));
gatewayDiamond.messenger().sendContractXnetMessage{value: DEFAULT_CROSS_MSG_FEE}(
TestUtils.newXnetCallMsg(
IPCAddress({
subnetId: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}),
rawAddress: FvmAddressHelper.from(caller)
}),
IPCAddress({subnetId: destinationSubnet, rawAddress: FvmAddressHelper.from(caller)}),
5,
0
)
);
}

// TODO: this is no longer possible because EOA cannot be subnet
// function testGatewayDiamond_SendCrossMessage_Fails_EoACaller() public {
// address caller = vm.addr(100);
Expand Down Expand Up @@ -1215,7 +1172,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT
gatewayDiamond.checkpointer().commitCheckpoint(checkpoint);

(, subnetInfo) = gatewayDiamond.getter().getSubnet(subnetId);
require(subnetInfo.circSupply == DEFAULT_COLLATERAL_AMOUNT - 10 * amount, "unexpected circulating supply");
// cross net messages with Call kind does not affect the circulating supply
require(subnetInfo.circSupply == DEFAULT_COLLATERAL_AMOUNT, "unexpected circulating supply");
}

function testGatewayDiamond_listIncompleteCheckpoints() public {
Expand Down

0 comments on commit a7c17ee

Please sign in to comment.