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

# Cross-Chain License Minting

> A guide on how to set up cross-chain license minting using deBridge.

In this tutorial, we will explore how to use [deBridge](https://docs.debridge.finance/) to perform cross-chain license minting. For this tutorial specifically, we'll be using Base \$ETH to mint a license on Story.

<CardGroup>
  <Card title="Base Example" icon="computer" color="#0000fe" href="https://github.com/jacob-tucker/base-story-quickstart">
    A working code example of minting a license on Story using Base \$ETH.
  </Card>

  <Card title="Abstract Chain Example" icon="computer" color="#2cdb84" href="https://github.com/jacob-tucker/abs-story-quickstart">
    A working code example of minting a license on Story using Abstract \$ETH.
  </Card>
</CardGroup>

From a high level, it involves:

1. Constructing a deBridge API call that will return tx data to swap tokens across chains and perform some action (e.g. mint a license on Story)
2. Executing the API call to receive that tx data
3. Executing the transaction (using the returned tx data) on the source chain

<Frame>
  <img src="https://mintcdn.com/story/VqUVNnzVygnCxrbT/images/tutorials/cross-chain-license-minting.jpeg?fit=max&auto=format&n=VqUVNnzVygnCxrbT&q=85&s=3c9b5876cae6a215c90caf7c96248f9f" alt="Cross-Chain License Minting" width="1878" height="1036" data-path="images/tutorials/cross-chain-license-minting.jpeg" />
</Frame>

## Step 1: Constructing the deBridge API Call

The first step is to construct a deBridge API call. The purpose of this API call is to receive back a response that will contain transaction data so we can then execute it on the source chain.

This deBridge order swaps tokens from one chain to another. We can also optionally attach a `dlnHook` that will execute an arbitrary action upon order completion (ex. after \$ETH has been swapped for \$IP). This is where the magic happens.

<Note>
  You can learn more about dlnHooks [here](https://docs.debridge.finance/dln-the-debridge-liquidity-network-protocol/integration-guidelines/interacting-with-the-api).
</Note>

In this case, the `dlnHook` will be a call to `mintLicenseTokensCrossChain`, which is a function [in this contract](https://www.storyscan.io/address/0x6429A616F76a8958e918145d64bf7681C3936D6A?tab=contract) that wraps the received \$IP to \$WIP and then mints a license token on Story.

You can see that `mintLicenseTokensCrossChain` looks like this:

```solidity DebridgeLicenseTokenMinter.sol expandable theme={null}
function mintLicenseTokensCrossChain(
    address licensorIpId,
    uint256 licenseTermsId,
    uint256 tokenAmount,
    address receiver
) external payable {
  uint256 amount = msg.value;
  require(amount > 0, "DebridgeLicenseTokenMinter: zero amount");

  // Create multicall data - using memory array directly to avoid stack issues
  IMulticall.Call3Value[] memory calls = new IMulticall.Call3Value[](2);

  // First call: deposit IP to get WIP tokens
  calls[0] = IMulticall.Call3Value({
      target: WIP,
      allowFailure: false,
      value: amount,
      callData: abi.encodeWithSelector(IWrappedIP.deposit.selector)
  });

  // Second call: mint license tokens
  calls[1] = IMulticall.Call3Value({
      target: LICENSING_MODULE,
      allowFailure: false,
      value: 0,
      callData: abi.encodeWithSelector(
          ILicensingModule.mintLicenseTokens.selector,
          licensorIpId,
          PIL_TEMPLATE,
          licenseTermsId,
          tokenAmount,
          receiver,
          "",
          0,
          100_000_000
      )
  });

  // Execute multicall and emit event
  IMulticall.Result[] memory returnData = IMulticall(MULTICALL).aggregate3Value{ value: amount }(calls);
  bytes memory raw = returnData[1].returnData;
  uint256 startLicenseTokenId = abi.decode(raw, (uint256)); // if mintLicenseTokens returns uint256
  emit LicenseTokensMinted(licensorIpId, receiver, startLicenseTokenId, tokenAmount);
}
```

You may be wondering, *"where does it `approve` the Royalty Module (what pays for the license minting fee) to spend the \$WIP?"* This is already done for you, since the Royalty Module is already approved to spend on behalf of Multicall.

The reason we had to make a Multicall contract in the first place, instead of simply bridging directly to \$WIP and calling `mintLicenseTokens` in the Licensing Module, is because the Royalty Module wouldn't be approved to spend the \$WIP. So we utilize the Multicall contract to take care of that for us instead.

<Tip>
  To summarize, we will construct a deBridge API call that says *"we want to swap \$ETH for \$IP, then use a dlnHook to call a smart contract on Story that wraps \$IP to \$WIP and mints a license on Story".*
</Tip>

### Step 1a. Constructing the `dlnHook`

The `dlnHook` is a JSON object that will be attached to the deBridge API call. It will contain the following information:

* The type of action to execute (`evm_transaction_call`)
* The address of the contract to call (`DebridgeLicenseTokenMinter.sol`)
* The calldata to execute (`mintLicenseTokensCrossChain`)

```typescript main.ts theme={null}
const STORY_WRAP_THEN_LICENSE_MULTICALL =
  "0x6429a616f76a8958e918145d64bf7681c3936d6a";

// Build the dlnHook for Story royalty payment
const buildRoyaltyPaymentHook = ({ ipId: `0x${string}`, licenseTermsId: bigint, receiverAddress: `0x${string}` }): string => {
  // Encode the mintLicenseTokensCrossChain function call
  const calldata = encodeFunctionData({
    abi: [
      {
        name: "mintLicenseTokensCrossChain",
        type: "function",
        inputs: [
          { name: "licensorIpId", type: "address" },
          { name: "licenseTermsId", type: "uint256" },
          { name: "tokenAmount", type: "uint256" },
          { name: "receiver", type: "address" },
        ],
      },
    ],
    functionName: "mintLicenseTokensCrossChain",
    args: [
      ipId,
      licenseTermsId,
      BigInt(1),
      receiverAddress,
    ],
  });

  // Build the dlnHook JSON
  const dlnHook = {
    type: "evm_transaction_call",
    data: {
      to: STORY_WRAP_THEN_LICENSE_MULTICALL,
      calldata: calldata,
      gas: 0,
    },
  };

  return JSON.stringify(dlnHook);
};
```

### Step 1b. Constructing the deBridge API Call

Now that we have the `dlnHook`, we can construct the whole deBridge API call, including the `dlnHook`.

<Note>
  You can view deBridge's documentation on the `create-tx` endpoint [here](https://docs.debridge.finance/dln-the-debridge-liquidity-network-protocol/integration-guidelines/interacting-with-the-api/creating-an-order). I also highly recommend checking out the [Swagger UI](https://dln.debridge.finance/v1.0#/DLN/DlnOrderControllerV10_createOrder) for the `create-tx` endpoint as well.
</Note>

| Attribute                       | Description                                                                                                                          |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `srcChainId`                    | The ID of the source blockchain (e.g., Ethereum mainnet is 1).                                                                       |
| `srcChainTokenIn`               | The address of the token being swapped on the source chain (ETH in this case).                                                       |
| `srcChainTokenInAmount`         | The amount of the source token to swap, set to `auto` for automatic calculation.                                                     |
| `dstChainId`                    | The ID of the destination blockchain (e.g., Story mainnet is 100000013).                                                             |
| `dstChainTokenOut`              | The address of the token to receive on the destination chain (WIP token).                                                            |
| `dstChainTokenOutAmount`        | The amount of the destination token to receive. It should be the same as the amount we're paying in `payRoyaltyOnBehalf` in step 1a. |
| `dstChainTokenOutRecipient`     | This can just be the same as `senderAddress`.                                                                                        |
| `senderAddress`                 | The address initiating the transaction.                                                                                              |
| `srcChainOrderAuthorityAddress` | The address authorized to manage the order on the source chain. This can just be the same as `senderAddress`.                        |
| `dstChainOrderAuthorityAddress` | The address authorized to manage the order on the destination chain. This can just be the same as `senderAddress`.                   |
| `enableEstimate`                | A flag to enable transaction simulation and estimation.                                                                              |
| `prependOperatingExpenses`      | A flag to include operating expenses in the transaction.                                                                             |
| `dlnHook`                       | The URL-encoded hook that specifies additional actions to execute post-swap.                                                         |

```typescript main.ts theme={null}
import { base } from "viem/chains";

// ... previous code here ...

// Build deBridge API URL for cross-chain royalty payment
const buildDeBridgeApiUrl = ({
  ipId: `0x${string}`,
  licenseTermsId: bigint,
  receiverAddress: `0x${string}`,
  senderAddress: `0x${string}`,
  paymentAmount: string // should be in wei
}): string => {
  const dlnHook = buildRoyaltyPaymentHook({ ipId, licenseTermsId, receiverAddress });
  const encodedHook = encodeURIComponent(dlnHook);
  const url =
    `https://dln.debridge.finance/v1.0/dln/order/create-tx?` +
    `srcChainId=${base.id}` +
    // we make this zero address which represents the native token ($ETH)
    `&srcChainTokenIn=0x0000000000000000000000000000000000000000` +
    // we set this to auto which will automatically calculate the amount of $ETH to swap
    `&srcChainTokenInAmount=auto` +
    // Story's mainnet chain ID
    `&dstChainId=100000013` +
    // we make this zero address which represents the native token ($IP)
    `&dstChainTokenOut=0x0000000000000000000000000000000000000000` +
    // we set the amount of $IP to pay for the license on Story
    `&dstChainTokenOutAmount=${paymentAmount}` +
    // the address of the contract that will wrap the $IP and mint the license on Story
    `&dstChainTokenOutRecipient=${senderAddress}` +
    // the address of the user initiating the transaction
    `&senderAddress=${senderAddress}` +
    // the address authorized to manage the order on the source chain
    `&srcChainOrderAuthorityAddress=${senderAddress}` +
    // the address authorized to manage the order on the destination chain
    `&dstChainOrderAuthorityAddress=${senderAddress}` +
    // we set this to true to enable transaction simulation and estimation
    `&enableEstimate=true` +
    // we set this to true to include operating expenses in the transaction
    `&prependOperatingExpenses=true` +
    `&dlnHook=${encodedHook}`;

  return url;
};
```

## Step 2: Executing the API Call

Once the API call is constructed, execute it to receive a response. This response includes transaction data and an estimate for running the transaction on the source swap chain (e.g., Ethereum, Solana).

<CodeGroup>
  ```typescript main.ts theme={null}
  // ... previous code here ...

  const getDeBridgeTransactionData = async ({
    ipId: `0x${string}`,
    licenseTermsId: bigint,
    receiverAddress: `0x${string}`,
    senderAddress: `0x${string}`,
    paymentAmount: string // should be in wei
  }): Promise<DeBridgeApiResponse> => {
    try {
      const apiUrl = buildDeBridgeApiUrl({ ipId, licenseTermsId, receiverAddress, senderAddress, paymentAmount });

      const response = await fetch(apiUrl, {
        method: "GET",
        headers: {
          Accept: "application/json",
        },
      });

      if (!response.ok) {
        throw new Error(
          `deBridge API error: ${response.status} ${response.statusText}`
        );
      }

      const data = (await response.json()) as DeBridgeApiResponse;

      // Validate the response
      if (!data.tx || !data.estimation || !data.orderId) {
        throw new Error("Invalid deBridge API response: missing required fields");
      }

      return data;
    } catch (error) {
      console.error("Error calling deBridge API:", error);
      throw error;
    }
  };
  ```

  ```typescript DeBridgeApiResponse theme={null}
  export interface DeBridgeApiResponse {
    estimation: {
      srcChainTokenIn: {
        amount: string;
        approximateOperatingExpense: string;
      };
      dstChainTokenOut: {
        amount: string;
        maxTheoreticalAmount: string;
      };
    };
    tx: {
      to: string;
      data: string;
      value: string;
    };
    orderId: string;
  }
  ```
</CodeGroup>

## Step 3: Executing the Transaction on the Source Chain

Next, you would take the API response and execute the transaction on the source chain.

<Note>
  View [the docs here](https://docs.debridge.finance/dln-the-debridge-liquidity-network-protocol/integration-guidelines/interacting-with-the-api/submitting-an-order-creation-transaction) on submitting the transaction, including how this would be done differently on Solana.
</Note>

```typescript TypeScript theme={null}
import { base } from "viem/chains";
import { createWalletClient, http, WalletClient, parseEther } from "viem";
import { privateKeyToAccount, Address, Account } from "viem/accounts";
import dotenv from "dotenv";

dotenv.config();

// Validate environment variables
if (!process.env.WALLET_PRIVATE_KEY) {
  throw new Error("WALLET_PRIVATE_KEY is required in .env file");
}

// Create account from private key
const account: Account = privateKeyToAccount(
  `0x${process.env.WALLET_PRIVATE_KEY}` as Address
);

// Initialize the wallet client
const walletClient = createWalletClient({
  chain: base,
  transport: http("https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID"), // Use Infura or another Ethereum provider
  account,
}) as WalletClient;

// ... previous code here ...

// Function to send a transaction
const executeLicenseMint = async (params: {
  ipId: `0x${string}`;
  licenseTermsId: bigint;
  receiverAddress: `0x${string}`;
  senderAddress: `0x${string}`;
  paymentAmount: string; // should be in wei
}) => {
  // Get transaction data from deBridge
  const deBridgeResponse = await getDeBridgeTransactionData(params);

  try {
    // Execute the transaction using the user's wallet
    const txHash = await walletClient.sendTransaction({
      to: deBridgeResponse.tx.to as Address,
      data: deBridgeResponse.tx.data as Address,
      value: BigInt(deBridgeResponse.tx.value),
      account: account as Account,
      chain: base,
    });
    console.log("Transaction sent:", txHash);

    // Wait for the transaction to be mined
    const receipt = await walletClient.waitForTransactionReceipt(txHash);
    console.log("Transaction mined:", receipt.transactionHash);
  } catch (error) {
    console.error("Error sending transaction:", error);
  }
};

// Example usage with a mock API response
const params = {
  ipId: "0xcb6B9CCae4108A103097B30cFc25e1E257D4b5Fe",
  licenseTermsId: BigInt(27910),
  receiverAddress: "0x01", // replace with the address that should receive the license on Story
  senderAddress: account.address,
  paymentAmount: parseEther("0.00001"), // 0.00001 $WIP since the license on Story costs 0.00001 $WIP
};

// Execute the function to send the transaction
executeLicenseMint(params);
```

## Conclusion

Congratulations! You have successfully set up cross-chain license minting using deBridge.

<CardGroup>
  <Card title="Base Example" icon="computer" color="#0000fe" href="https://github.com/jacob-tucker/base-story-quickstart">
    A working code example of minting a license on Story using Base \$ETH.
  </Card>

  <Card title="Abstract Chain Example" icon="computer" color="#2cdb84" href="https://github.com/jacob-tucker/abs-story-quickstart">
    A working code example of minting a license on Story using Abstract \$ETH.
  </Card>
</CardGroup>
