Examples

To illustrate the flexibility of the standard, here are some more examples of how ResolvedCrossChainOrder could be defined:

Across + Permit2 Example

A cross chain order on Across has a single input and output token and fillers do not get the full inputAmount refunded. Instead, an Across contract on the originChainId would define a function like getFillerFee(order, repaymentChainId) to deterministically compute the fee charged to them. Fillers on Across can choose where they want to receive their refund.

This example settlement contract contains a fillCrossChainOrder in the SettlementContract that fillers can use to fulfill initiated orders. Fills for other settlement systems might be submitted to a different contract than the settlement contract itself.

AcrossSettlementContract is ISettlementContract {

	// Unique Across nonce
	uint256 depositId;
	// Permit2 contract for this network
	address constant PERMIT2;

	// Data unique to every CrossChainOrder settled on Across
	struct AcrossOrderData {
		uint32 exclusivityDeadline;
		address exclusiveRelayer;
		bytes message;
	}

	// Data unique to every attempted order fulfillment
	struct AcrossFillerData {
		// Filler can choose where they want to be repaid
	  uint256 repaymentChainId;
	}

	function initiate(CrossChainOrder order, bytes signature, bytes fillerData) external {  
	  // Ensure that order was intended to be settled by Across.
	  require(order.settlementContract == address(this));
	  require(order.originChainId == block.chainId);
  
	  // Extract Across-specific params.
	  (resolvedOrder, acrossOrderData) = _resolve(order, fillerData);

		 // Verify Permit2 signature and pull user funds into this contract
		_processPermit2Order(PERMIT2, order, resolvedOrder, signature);
	
		// Emit Across-specific event used for settlement.
		emit FundsDepositedV3(
			resolvedOrder.swapperInputs[0].token,
			resolvedOrder.outputs[0].token,
			resolvedOrder.swapperInputs[0].amount,
			resolvedOrder.outputs[0].amount,
			resolvedOrder.outputs[0].chainId,
			depositId++, // Unique Across nonce
			block.timestamp,
			order.fillDeadline,
			acrossOrderData.exclusivityDeadline,
			order.swapper
			resolvedOrder.outputs[0].recipient,
			acrossOrderData.exclusiveRelayer,
			acrossOrderData.message
		);
	}

	function resolve(CrossChainOrder order, bytes fillerData) external view returns (ResolvedCrossChainOrder) {    
	  (resolvedOrder, ) = _resolve(order, fillerData);
	}

	// Filler calls this function on the destinationChainId to fulfill an order.
	// This function would not always be defined in this SettlementContract, but
	// for illustrative purposes it is included here.
	// (In most cases, the full `order` and `fillerData` wouldn't need to be supplied
	// here, instead a subset would suffice).
	function fillCrossChainOrder(CrossChainOrder order, bytes fillerData) external {
		// Ensure order has not expired
		require(order.fillDeadline >= block.timestamp)
	
	  (, acrossOrderData, acrossFillerData) = _resolve(order, fillerData);
	  
	  // Pull tokens from filler to fill recipient
	  IERC20(resolvedOrder.outputs[0].token).transferFrom(
		  msg.sender, 
		  address(this), 
		  resolvedOrder.outputs[0].amount
		);
		IERC20(crossChainOrder.outputs[0].token).transfer(
		  resolvedOrder.outputs[0].recipient, 
		  resolvedOrder.outputs[0].amount
		);
  
	  // Signal to settlement contract that the cross chain order has been fulfilled
	  // using a combination of standardized order data and Across-specific data decoded
	  // from the standardized order data.
	  emit FilledRelayV3(
	    crossChainOrder.swapperInputs[0].token,
      crossChainOrder.swapperOutputs[0].token,
      crossChainOrder.swapperInputs[0].amount,
      crossChainOrder.outputs[0].amount,
      acrossFillerData.repaymentChainId,
      order.originChainId,
      acrossOrderData.depositId,
      order.fillDeadline,
      acrossOrderData.exclusivityDeadline,
      acrossOrderData.exclusiveRelayer,
      msg.sender,
      order.swapper,
      acrossOrderData.recipient,
      acrossOrderData.message
	  );
}

function _resolve(CrossChainOrder order, bytes fillerData) internal 
	returns(
		AcrossOrderData acrossOrderData, 
		ResolvedCrossChainOrder resolvedCrossChainOrder,
		AcrossFillerData acrossFillerData
	) {
	// Extract Across-specific params.
	acrossOrderData = abi.decode(order.orderData, (AcrossOrderData));
	
	// Compute filler fee using filler-provided data.
	acrossFillerData = abi.decode(fillerData, (AcrossFillerData));
	Output memory fee = FeeCalculator.computeFee(
			order.originChainId, 
			acrossFillerData.repaymentChainId,
			acrossOrderData.inputToken,
			acrossOrderData.inputAmount
	);
		
	resolvedCrossChainOrder = ResolvedCrossChainOrder ({
			settlementContract: address(this);
			swapper: order.swapper;
			nonce: order.nonce;
			originChainId: order.originChainId;
			initiateDeadline: order.initiateDeadline;
			fillDeadline: order.fillDeadline;
			swapperInputs: [Input({ 
				token: acrossOrderData.inputToken,
				amount: acrossOrderData.inputAmount,
				maximumAmount: acrossOrderData.inputAmount
			})],
			swapperOutputs: [Output({ 
				token: acrossOrderData.outputToken,
				amount: acrossOrderData.outputAmount,
				recipient: acrossOrderData.recipient,
				chainId: acrossOrderData.destinationChainId
			})],
			fillerOutputs: [Output({ 
				token: fee.token,
				amount: acrossOrderData.inputAmount - fee.amount,
				recipient: acrossOrderData.recipient,
				chainId: fee.chainId
			})]
}

function _processPermit2Order(
	IPermit2 permit2,
	CrossChainOrder order, 
	ResolvedCrossChain resolvedOrder, 
	bytes signature
) internal {
	  IPermit2.PermitTransferFrom memory permit = IPermit2.PermitTransferFrom({
	    permitted: IPermit2.TokenPermissions({ token: resolvedOrder.swapperInputs[0].token, amount: resolvedOrder.swapperInputs[0].maxAmount }),
      nonce: order.nonce,
      deadline: order.initiateDeadline
    });

    IPermit2.SignatureTransferDetails memory signatureTransferDetails = IPermit2.SignatureTransferDetails({
      to: address(this),
      requestedAmount: resolvedOrder.inputs[0].amount
    });

    // Pull user funds.
    permit2.permitWitnessTransferFrom(
      permit,
      signatureTransferDetails,
      order.swapper,
      _hash(order), // witness data hash
      PERMIT2_ORDER_TYPE, // witness data type string
      signature
    );
}

Last updated