> For the complete documentation index, see [llms.txt](https://docs.symm.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.symm.io/liquidity-provider-documentation/building-a-solver-on-symmio/4.-opening-closing-a-position-on-chain.md).

# 4. Opening/Closing a Position On-Chain

## 4. Opening/Closing a Position On-Chain

### Opening a position

At this stage, the solver calls `openPosition()` or a combined function (`lockAndOpenQuote()`).

* Partial fills are possible: if the hedger only wants to open half the quantity, the remainder can be reposted as a new intent.
* The contract runs a final solvency check via Muon. If either party is insolvent, the contract disallows opening.

Before a hedger can `lockQuote()` or `openPosition()`, enough collateral must be allocated to PartyB. This allocation ensures PartyB meets the margin requirements for the position. If the allocated balance is insufficient, the contract rejects the `lockQuote` or `openPosition` transaction.

#### Allocating collateral for PartyB

A short script showing how a solver allocates collateral to a PartyA. There's more [here](/contract-documentation/symmio-perps-v0.8.5/diamond-core-facets/partyb-account.md).

```javascript
async function allocateForPartyB(amount, partyB, partyA) {
  try {
    console.log(`Allocating ${amount} tokens for PartyB with address ${partyA} from ${partyB}...`);

    // Estimate gas for the transaction
    const allocateGasEstimate = await diamondContract.methods.allocateForPartyB(amount, partyA).estimateGas({ from: partyB });
    console.log("Estimated Gas: ", allocateGasEstimate);

    // Fetch current gas price
    const allocateGasPrice = await web3.eth.getGasPrice();
    console.log("Current Gas Price: ", allocateGasPrice);

    // Execute the allocation transaction
    const receipt = await diamondContract.methods.allocateForPartyB(amount, partyA).send({
      from: partyB,
      gas: allocateGasEstimate,
      gasPrice: allocateGasPrice,
    });

    console.log("Allocation successful!");
    return { success: true, receipt: receipt };
  } catch (error) {
    console.error("Error during allocation:", error);
    return { success: false, error: error.toString() };
  }
}
```

#### Locking the quote

Locking the quote ensures no other hedger can act on the same quoteId. It requires a valid Muon signature for the position's uPnl (`uPnl_B`); there's more on that method here. Pass the formatted signature and quoteId to `lockQuote()`.

```javascript
async function lockQuote(accountAddress, partyA, quoteId, increaseNonce, muonUrls, chainId, diamondAddress) {
  const lockQuoteClient = LockQuoteClient.createInstance(true);
  const signatureResult = await lockQuoteClient.getMuonSig(accountAddress, partyA, 'symmio', muonUrls, chainId, diamondAddress);

  if (signatureResult.success) {
    const { reqId, timestamp, upnl, gatewaySignature, sigs } = signatureResult.signature;
    const upnlSigFormatted = {
      reqId: web3.utils.hexToBytes(reqId),
      timestamp: timestamp.toString(),
      upnl: upnl.toString(),
      gatewaySignature: web3.utils.hexToBytes(gatewaySignature),
      sigs: {
        signature: sigs.signature.toString(),
        owner: sigs.owner,
        nonce: sigs.nonce,
      },
    };

    const gasEstimate = await diamondContract.methods.lockQuote(quoteId, upnlSigFormatted, increaseNonce).estimateGas({ from: accountAddress });
    const gasPrice = await web3.eth.getGasPrice();

    const receipt = await diamondContract.methods.lockQuote(quoteId, upnlSigFormatted, increaseNonce).send({
      from: accountAddress,
      gas: gasEstimate,
      gasPrice,
    });

    console.log("Lock Quote successful!", receipt);
  } else {
    console.error("Failed to fetch Muon signature:", signatureResult.error);
  }
}
```

#### Opening the position

Once the quote is locked, the solver calls `openPosition()` to finalize the transaction. This method runs a solvency check before opening.

```javascript
async function openPosition(accountAddress, partyA, quoteId, filledAmount, openedPrice, symbolId, muonUrls, chainId, diamondAddress) {
  const openPositionClient = OpenPositionClient.createInstance(true);
  const signatureResult = await openPositionClient.getPairUpnlAndPriceSig(accountAddress, partyA, symbolId, 'symmio', muonUrls, chainId, diamondAddress);

  if (signatureResult.success) {
    const { reqId, timestamp, uPnlA, uPnlB, price, gatewaySignature, sigs } = signatureResult.signature;
    const upnlSigFormatted = {
      reqId: web3.utils.hexToBytes(reqId),
      timestamp: timestamp.toString(),
      upnlPartyA: uPnlA.toString(),
      upnlPartyB: uPnlB.toString(),
      price: price.toString(),
      gatewaySignature: web3.utils.hexToBytes(gatewaySignature),
      sigs: {
        signature: sigs.signature.toString(),
        owner: sigs.owner,
        nonce: sigs.nonce,
      },
    };

    const gasEstimate = await diamondContract.methods.openPosition(quoteId, filledAmount, openedPrice, upnlSigFormatted).estimateGas({ from: accountAddress });
    const gasPrice = await web3.eth.getGasPrice();

    const receipt = await diamondContract.methods.openPosition(quoteId, filledAmount, openedPrice, upnlSigFormatted).send({
      from: accountAddress,
      gas: gasEstimate,
      gasPrice,
    });

    console.log("Open Position successful!", receipt);
  } else {
    console.error("Failed to fetch Muon signature:", signatureResult.error);
  }
}
```

The Muon `uPnlWithSymbolPrice` method is used for solvency verification. On success, the `positionState` changes to OPENED. If either party is insolvent, the contract rejects the transaction.

#### Combined method: `lockAndOpenQuote()`

For efficiency, solvers can use combined methods like `lockAndOpenQuote()`, which run the locking and opening steps in a single transaction.

```solidity
	/**
	 * @notice Locks and opens the specified quote with the provided details and signatures.
	 * @param quoteId The ID of the quote to be locked and opened.
	 * @param filledAmount PartyB has the option to open the position with either the full amount requested by the user or a specific fraction of it
	 * @param openedPrice The price at which the position is opened.
	 * @param upnlSig The Muon signature containing the single UPNL value used to lock the quote.
	 * @param pairUpnlSig The Muon signature containing the pair UPNL and price values used to open the position.
	 */
	function lockAndOpenQuote(
		uint256 quoteId,
		uint256 filledAmount,
		uint256 openedPrice,
		SingleUpnlSig memory upnlSig,
		PairUpnlAndPriceSig memory pairUpnlSig
	) external whenNotPartyBActionsPaused onlyPartyB notLiquidated(quoteId) {}
```

{% hint style="info" %}
Both a `uPnlSig` (from uPnL\_B) and a `pairUpnlSig` (from uPnlWithSymbolPrice) are required for this group action.
{% endhint %}

Once opened, the position stays active until fully closed or liquidated. PartyA can place limit or market close requests, which the solver fills. If the solver doesn't respond within a certain timeframe, PartyA can force close or force cancel.

### Closing a position

PartyA calls `requestToCloseQuote()` with the parameters (quantity, price if limit, and so on). The solver can close its corresponding off-chain hedge, then call `fillCloseRequest()` on-chain.

Closing a position on Symmio means calling `fillCloseRequest()`. The solver (PartyB) can close an open position partially or fully, depending on the request type and amount.

#### How fillCloseRequest works

Parameters:

* `quoteId`: the unique identifier of the quote to close.
* `filledAmount`: the amount being closed.
* `closedPrice`: the final price at which the position closes.
* `upnlSig`: a Muon signature that ensures solvency and validates the close.

`LIMIT` requests can be closed incrementally in multiple steps; `MARKET` requests must be closed in a single transaction.

#### Preconditions for closing

* The hedger (PartyB) must be authorized to act on the quote. If not, contact the SYMMIO developers about whitelisting.
* A Muon signature validates solvency for both parties before the close is processed.
* The position must not be in the `LIQUIDATED` state, and PartyA must be solvent after the close.

#### Closing the position script

```javascript
const Web3 = require('web3');
const OpenPositionClient = require('./OpenPositionClient'); // Assuming OpenPositionClient exists in your setup
const { abi } = require('./DiamondContractABI.json'); // ABI for the diamond contract

async function fillCloseRequest(accountAddress, partyA, partyB, quoteId, filledAmount, closedPrice, symbolId, muonUrls, chainId, diamondAddress) {
  console.log("Filling close request...");

  const closeRequestClient = OpenPositionClient.createInstance(true);
  if (!closeRequestClient) {
    console.error("OpenPositionClient is not enabled.");
    return { success: false, error: "Initialization failed" };
  }

  const web3 = new Web3(process.env.RPC_URL);
  const diamondContract = new web3.eth.Contract(abi, diamondAddress);
  const appName = "symmio";
  const urls = [muonUrls];

  try {
    // Fetch Muon signature
    const signatureResult = await closeRequestClient.getPairUpnlAndPriceSig(accountAddress, partyA, symbolId, appName, urls, chainId, diamondAddress);
    if (!signatureResult.success) {
      throw new Error(signatureResult.error || "Muon signature fetch failed");
    }

    const { reqId, timestamp, uPnlA, uPnlB, price, gatewaySignature, sigs } = signatureResult.signature;
    const upnlSigFormatted = {
      reqId: web3.utils.hexToBytes(reqId),
      timestamp: timestamp.toString(),
      upnlPartyA: uPnlA.toString(),
      upnlPartyB: uPnlB.toString(),
      price: price.toString(),
      gatewaySignature: web3.utils.hexToBytes(gatewaySignature),
      sigs: {
        signature: sigs.signature.toString(),
        owner: sigs.owner,
        nonce: sigs.nonce,
      },
    };

    // Call fillCloseRequest
    const receipt = await diamondContract.methods.fillCloseRequest(
      quoteId,
      filledAmount,
      closedPrice,
      upnlSigFormatted
    ).send({
      from: partyB,
      gas: 500000,
    });

    console.log("Close request successfully filled!");
    return { success: true, receipt };
  } catch (error) {
    console.error("Error filling close request:", error);
    return { success: false, error: error.toString() };
  }
}

module.exports = fillCloseRequest;

```

This script lets **PartyB** close a trade initiated by **PartyA** via `fillCloseRequest()` on the smart contract. The Muon signature is used for solvency validation; the correct Muon method here is also `uPnlWithSymbolPrice`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.symm.io/liquidity-provider-documentation/building-a-solver-on-symmio/4.-opening-closing-a-position-on-chain.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
