> ## Documentation Index
> Fetch the complete documentation index at: https://docs.swaps.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Best Practices

> Get the most out of Swaps with our multi-chain development best practices.

## Pay attention to named vs. alt VMs

We classify chains by their execution environment. Our routing and transaction tracking primarily differs based on whether chains in your quote request include named vs. alt VMs. The `/getAction` response includes a [`vmId` field](/swap-api-reference/get-action#response-vm-id) that informs you which routing strategy will applies to your quote. You can also find this information on the Supported Chains page.

**When the `vmId` is `alt-vm`, please note that you must:**

1. Use the `/getPaths` endpoint to validate your destination token andj source token swap amount ([reference](/resources/best-practices#cache-available-paths)).
2. Register your transaction after broadcasting to initiate tracking ([reference](/guides/track-transactions#register-transaction)).

## Cache available paths

The [`/getPaths` endpoint](/swap-api-reference/get-paths) returns all available destinations for a given source token and set of filters. For alternative VMs, there may be minimum or maximum transfer amounts, or only a subset of the total tokens available on a network may be supported. We recommend caching available paths with \~15 minute expiries and checking against it as part of the [quoting process](/guides/swap#create-a-swap-transaction) to ensure your requests are executable.

For named VMs, it is safe to assume any token in any size is available.

## Manage permissioned functions

Some smart contracts include permissioned functions that resolve based on the wallet address sending the transaction. Often, you will see a check based on the `msg.sender`.

Cross-chain transactions are executed from relayer accounts, not directly from the user's wallet. As a result, a `msg.sender` check will fail even if it *actually is* the authorized wallet address sending the source chain transaction.

`msg.sender` checks are typically impractical in a multi-chain context. Instead, we recommend a signature-based approach to authentication.

For example, the [Songcamp](https://song.camp/) team wanted users to mint NFTs without metadata and then send a permissioned function to write metadata to the NFT based on certain traits of the user's wallet address.

To enable cross-chain execution while still authenticating wallets, Songcamp included a hash of the user's wallet address in the transaction calldata and recovered the singer's address as follows:

[View full contract](https://explorer.zora.energy/address/0x38898cadb5241121620a81e7bca47eab8a87402a?tab=contract)

```solidity theme={null}
function multiWriteToDiscSignature( // [!code focus]
    uint256[] memory tokenIds, // [!code focus]
    uint256[] memory songSelections, // [!code focus]
    bytes memory signature  // [!code focus]
) public {
    require(
        tokenIds.length == songSelections.length,
        "tokenIds and songSelections arrays must have the same length"
    );

    //Constructing the signed hash for signer address recovery  // [!code focus]
    bytes32 messageHash = keccak256(abi.encodePacked(tokenIds, songSelections));  // [!code focus]

    bytes32 ethSignedHash = keccak256(  // [!code focus]
        abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash)  // [!code focus]
    );  // [!code focus]

    address signer = recoverSigner(ethSignedHash, signature);  // [!code focus]

    for (uint256 i = 0; i < tokenIds.length; i++) {
        uint256 tokenId = tokenIds[i];
        uint256 songChoiceId = songSelections[i];

        // Check if CdMemory is not written before allowing an update
        CdMemory storage cd = readCdMemory[tokenId];
        require(
            ownerOf(tokenId) == signer,
            "Only the owner can set CdMemory"
        );
        require(
            songChoiceId >= 1 && songChoiceId <= 5,
            "Invalid song choice ID"
        );
        require(!cd.written, "One or more tokens are already written.");

        // Update CdMemory and mark it as written
        cd.writerAddress = signer;
        cd.songChoiceId = songChoiceId;
        cd.written = true;

        writeCount += 1;

        emit CdMemorySet(tokenId, signer, songChoiceId);
    }
}

//Helper function to determine signer address based on the signed hash and the signature

function recoverSigner( // [!code focus]
    bytes32 ethSignedHash, // [!code focus]
    bytes memory signature // [!code focus]
) public pure returns (address) { // [!code focus]
    // Extract the r, s, and v values from the signature // [!code focus]
    (bytes32 r, bytes32 s, uint8 v) = splitSignature(signature); // [!code focus]

    // Recover and return the signer address // [!code focus]
    return ecrecover(ethSignedHash, v, r, s); // [!code focus]
}

//Helper Function to split signature into RSV values // [!code focus]
function splitSignature( // [!code focus]
    bytes memory signature // [!code focus]
) public pure returns (bytes32 r, bytes32 s, uint8 v) { // [!code focus]
    require(signature.length == 65, "Invalid signature length"); // [!code focus]

    assembly { // [!code focus]
        // Slice the r, s, and v components from the signature // [!code focus]
        r := mload(add(signature, 32)) // [!code focus]
        s := mload(add(signature, 64)) // [!code focus]
        v := byte(0, mload(add(signature, 96))) // [!code focus]
    } // [!code focus]
} // [!code focus]
```
