> For the complete documentation index, see [llms.txt](https://docs.tronsave.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tronsave.io/chinese/developers/api-reference/sell-resources.md).

# 出售资源

出售资源 API 让资源供应商可以在 TronSave 上手动成交未完成的买单。卖家获取活跃订单，构建并签名一笔链上 `DelegateResourceContract` 交易，然后将签名交易提交给 TronSave 进行订单撮合与结算。

{% hint style="warning" %}
**需要白名单。** 出售 API 仅对加入白名单的钱包开放。请登录 TronSave 并将您的钱包地址发送给客服，以便将其加入白名单。加入白名单后，使用从该钱包地址生成的 API 密钥来调用出售相关端点。

通过 Telegram 联系客服：[**@wantingtrx**](https://t.me/wantingtrx)
{% endhint %}

出售是一个两步流程：

1. [**获取活跃订单**](#step-1-get-active-orders) — 列出您可以成交的未完成买单。
2. [**出售资源**](#step-2-sell-the-resource) — 针对选定的订单提交一笔签名代理交易。

## Step 1: 获取活跃订单

返回可供撮合的全局活跃买单列表。

<mark style="color:blue;">**`GET`**</mark> **`https://api.tronsave.io/v2/orders/active-global`**

{% hint style="info" %}
**速率限制：** 每 1 秒 3 次请求。
{% endhint %}

### Headers

<table><thead><tr><th width="160">Name</th><th width="120">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>apikey</code> <mark style="color:red;">*</mark></td><td>String</td><td>TronSave API 密钥（必须属于白名单地址）。参见 <a href="/pages/pQAbgL9zZkYLsxp1zRMt">身份验证</a>。</td></tr></tbody></table>

### 查询参数

<table><thead><tr><th width="221">Name</th><th width="172">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>page</code></td><td>Integer</td><td>页索引，从 0 开始。默认值：<code>0</code></td></tr><tr><td><code>pageSize</code></td><td>Integer</td><td>每页订单数量。默认值：<code>10</code></td></tr></tbody></table>

### 响应

```javascript
{
  "error": false,
  "message": "Success",
  "data": {
    "data": [
      {
        "id": string,            // Order identifier — use as orderId in Step 2
        "receiver": string,      // TRON address that receives the delegated resource
        "resourceAmount": number,// the amount of resource
        "resourceType": string,  // the resource type is "ENERGY" or "BANDWIDTH"
        "remainAmount": number,  // the remaining amount that can be matched by the system
        "orderType": string,     // type of order is "NORMAL" or "EXTEND"
        "price": number,         // price unit is equal to SUN
        "durationSec": number,   // rent duration, duration unit is equal to seconds
        "status": string,        // the order status, either Active or Completed
        "allowPartialFill": boolean, // Allow the order to be filled partially or not
        "payoutAmount": number,  // Total payout of this order (SUN)
        "fulfilledPercent": number, // The percent that shows filling processing. 0-100
        "createdAt": 1778467289
      }
    ],
    "total": 3
  }
}
```

**响应示例**

```javascript
{
  "error": false,
  "message": "Success",
  "data": {
    "data": [
      {
        "id": "6a0141d98e6f3f91d2284444",
        "receiver": "TA4JgKPtrPLtGQ1WMz3qmorqbL22221111",
        "resourceAmount": 500000,
        "resourceType": "ENERGY",
        "remainAmount": 339999,
        "orderType": "NORMAL",
        "price": 50,
        "durationSec": 259200,
        "status": "Active",
        "allowPartialFill": true,
        "payoutAmount": 24000150,
        "fulfilledPercent": 32,
        "createdAt": 1778467289
      }
    ],
    "total": 3
  }
}
```

### 请求示例

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X GET \
  "https://api.tronsave.io/v2/orders/active-global?page=0&pageSize=10" \
  -H "apikey: YOUR_API_KEY"
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const res = await fetch(
  "https://api.tronsave.io/v2/orders/active-global?page=0&pageSize=10",
  {
    method: "GET",
    headers: {
      apikey: "YOUR_API_KEY",
      "content-type": "application/json",
    },
  }
);

const data = await res.json();
console.log(data.data.data);
```

{% endtab %}

{% tab title="Python" %}

```python
import urllib.parse
import urllib.request
import json

query = urllib.parse.urlencode({"page": 0, "pageSize": 10})
url = f"https://api.tronsave.io/v2/orders/active-global?{query}"

req = urllib.request.Request(
    url,
    method="GET",
    headers={"apikey": "YOUR_API_KEY", "content-type": "application/json"},
)
with urllib.request.urlopen(req) as response:
    data = json.loads(response.read().decode("utf-8"))
print(data["data"]["data"])
```

{% endtab %}

{% tab title="Java" %}

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class GetActiveOrders {
    public static void main(String[] args) throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.tronsave.io/v2/orders/active-global?page=0&pageSize=10"))
            .header("apikey", "YOUR_API_KEY")
            .header("content-type", "application/json")
            .GET()
            .build();

        HttpResponse<String> response =
            client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());
    }
}
```

{% endtab %}

{% tab title="Go" %}

```go
package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	url := "https://api.tronsave.io/v2/orders/active-global?page=0&pageSize=10"
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("apikey", "YOUR_API_KEY")
	req.Header.Set("content-type", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
}
```

{% endtab %}

{% tab title="Rust" %}

```rust
// Cargo.toml:
// reqwest = { version = "0.12", features = ["blocking", "json"] }
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    let client = reqwest::blocking::Client::new();
    let res = client
        .get("https://api.tronsave.io/v2/orders/active-global")
        .query(&[("page", "0"), ("pageSize", "10")])
        .header("apikey", "YOUR_API_KEY")
        .header("content-type", "application/json")
        .send()?;

    let body: serde_json::Value = res.json()?;
    println!("{}", body);
    Ok(())
}
```

{% endtab %}
{% endtabs %}

## Step 2: 出售资源

提交一笔签名代理交易以成交选定的订单。

<mark style="color:orange;">**`POST`**</mark> **`https://api.tronsave.io/v2/orders/sell-manual`**

{% hint style="info" %}
**速率限制：** 每 1 秒 2 次请求。
{% endhint %}

### Headers

<table><thead><tr><th width="160">Name</th><th width="120">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>apikey</code> <mark style="color:red;">*</mark></td><td>String</td><td>TronSave API 密钥（必须属于白名单地址）。参见 <a href="/pages/pQAbgL9zZkYLsxp1zRMt">身份验证</a>。</td></tr></tbody></table>

### 请求体

<table><thead><tr><th width="268">Name</th><th width="122">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>orderId</code> <mark style="color:red;">*</mark></td><td>string</td><td>来自 <strong>Step 1</strong> 中 <code>id</code> 字段的订单 ID。</td></tr><tr><td><code>paymentAddress</code></td><td>string</td><td>接收出售订单付款的 TRON 地址。如果未提供，付款将发送至委托方（delegator）地址。</td></tr><tr><td><code>isAllowSellForLockedDelegator</code></td><td>boolean</td><td><code>false</code>（默认）— 不出售给当前在链上被锁定的目标地址。<br><code>true</code> — 出售给所有目标地址。</td></tr><tr><td><code>signedTx</code> <mark style="color:red;">*</mark></td><td>object</td><td>签名交易。请注意它是一个 JSON 对象。</td></tr></tbody></table>

### 请求体示例

```javascript
{
  "orderId": "6a0141d98e6f3f91d2284444",
  "paymentAddress": "TCtk4viKFyywGkxmPSLLTfsBqrGPFFFFFF",
  "isAllowSellForLockedDelegator": false,
  "signedTx": {
    "visible": false,
    "txID": "997ce381e04206eac74ab911bc21ded3bc1e6a1dc032d900e9e690b549933333",
    "raw_data_hex": "0a026cc42208738b6bf2033b649740b0f594b0e1335a79083912750a35747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e44656c65676174655265736f75726365436f6e7472616374123c0a1541be35e6bcd33e46894072909d72bd31ccd93b1b73100118a6a1bc810522154100f6d191a641af2c015b52b1ffef788a3352cef628013080a30570d0a091b0e133",
    "raw_data": {
      "contract": [
        {
          "parameter": {
            "value": {
              "owner_address": "41be35e6bcd33e46894072909d72bd31ccd93b1b73",
              "receiver_address": "4100f6d191a641af2c015b52b1ffef788a3352cef6",
              "balance": 1345261734,
              "resource": "ENERGY",
              "lock": true,
              "lock_period": 86400
            },
            "type_url": "type.googleapis.com/protocol.DelegateResourceContract"
          },
          "type": "DelegateResourceContract"
        }
      ],
      "ref_block_bytes": "6cc4",
      "ref_block_hash": "738b6bf2033b6497",
      "expiration": 1778485902000,
      "timestamp": 1778485842000
    },
    "signature": [
      "xxxxxxxxx"
    ]
  }
}
```

### 响应（成功）

```javascript
{
  "error": false,
  "message": "Success",
  "data": {
    "success": true,
    "message": "Sell manual success",
    "code": 200,
    "delegatedId": "6a015f126da945e2f7773333"
  }
}
```

### 响应（错误）

两个出售端点都要求提供属于白名单地址的有效 `apikey` 请求头。身份验证和校验失败会返回下列响应体之一。

**401 Unauthorized — 缺少 API 密钥**（没有 `apikey` 请求头）：

```json
{
  "error": true,
  "message": "TSAS:106 API_KEY_REQUIRED",
  "data": null
}
```

**401 Unauthorized — 无效的 API 密钥**：

```json
{
  "error": true,
  "message": "TSAS:107 INVALID_API_KEY",
  "data": null
}
```

**400 Bad Request — 请求体校验失败**（缺少必填字段或字段无效；`message` 会指出出错的字段，例如 `orderId` 或 `signedTx`）：

```json
{
  "statusCode": 400,
  "code": "FST_ERR_VALIDATION",
  "error": "Bad Request",
  "message": "body must have required property 'orderId'"
}
```

**404 Not Found — 路由/路径错误**：

```json
{
  "message": "Route POST:/v2/... not found",
  "error": "Not Found",
  "statusCode": 404
}
```

业务逻辑失败（例如所选订单已无法成交，或签名交易与订单不匹配）会返回标准的 TronSave 响应封装，其中 `error` 为 `true` 且 `message` 包含描述性说明：

```javascript
{
  "error": true,
  "message": "<error description>"
}
```

{% hint style="info" %}
**速率限制：** 全局默认限制为每秒 15 次请求；出售资源端点限制为每秒 2–3 次请求（`active-global` 为每秒 3 次，`sell-manual` 为每秒 2 次）。超过限制会返回 `{"error": true, "message": "Rate limit reached"}`。
{% endhint %}

### 请求示例

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X POST \
  "https://api.tronsave.io/v2/orders/sell-manual" \
  -H "apikey: YOUR_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "orderId": "6a0141d98e6f3f91d2284444",
    "paymentAddress": "YOUR_TRON_ADDRESS",
    "isAllowSellForLockedDelegator": false,
    "signedTx": {
      "visible": false,
      "txID": "997ce381e04206eac74ab911bc21ded3bc1e6a1dc032d900e9e690b549933333",
      "raw_data_hex": "0a026cc4...",
      "raw_data": { },
      "signature": ["xxxxxxxxx"]
    }
  }'
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const body = {
  orderId: "6a0141d98e6f3f91d2284444",
  paymentAddress: "YOUR_TRON_ADDRESS",
  isAllowSellForLockedDelegator: false,
  signedTx, // signed DelegateResourceContract object (e.g. from TronWeb)
};

const res = await fetch("https://api.tronsave.io/v2/orders/sell-manual", {
  method: "POST",
  headers: {
    apikey: "YOUR_API_KEY",
    "content-type": "application/json",
  },
  body: JSON.stringify(body),
});

const data = await res.json();
console.log(data);
```

{% endtab %}

{% tab title="Python" %}

```python
import urllib.request
import json

signed_tx = {
    "visible": False,
    "txID": "997ce381e04206eac74ab911bc21ded3bc1e6a1dc032d900e9e690b549933333",
    "raw_data_hex": "0a026cc4...",
    "raw_data": {},
    "signature": ["xxxxxxxxx"],
}

payload = {
    "orderId": "6a0141d98e6f3f91d2284444",
    "paymentAddress": "YOUR_TRON_ADDRESS",
    "isAllowSellForLockedDelegator": False,
    "signedTx": signed_tx,
}

req = urllib.request.Request(
    "https://api.tronsave.io/v2/orders/sell-manual",
    method="POST",
    data=json.dumps(payload).encode("utf-8"),
    headers={"apikey": "YOUR_API_KEY", "content-type": "application/json"},
)
with urllib.request.urlopen(req) as response:
    data = json.loads(response.read().decode("utf-8"))
print(data)
```

{% endtab %}

{% tab title="Java" %}

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class SellManual {
    public static void main(String[] args) throws Exception {
        // signedTx must be a JSON object (e.g. produced by a TRON SDK)
        String body = """
            {
              "orderId": "6a0141d98e6f3f91d2284444",
              "paymentAddress": "YOUR_TRON_ADDRESS",
              "isAllowSellForLockedDelegator": false,
              "signedTx": {
                "visible": false,
                "txID": "997ce381e04206eac74ab911bc21ded3bc1e6a1dc032d900e9e690b549933333",
                "raw_data_hex": "0a026cc4...",
                "raw_data": { },
                "signature": ["xxxxxxxxx"]
              }
            }
            """;

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.tronsave.io/v2/orders/sell-manual"))
            .header("apikey", "YOUR_API_KEY")
            .header("content-type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();

        HttpResponse<String> response =
            client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());
    }
}
```

{% endtab %}

{% tab title="Go" %}

```go
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	// signedTx must be a JSON object (e.g. produced by a TRON SDK)
	body := []byte(`{
		"orderId": "6a0141d98e6f3f91d2284444",
		"paymentAddress": "YOUR_TRON_ADDRESS",
		"isAllowSellForLockedDelegator": false,
		"signedTx": {
			"visible": false,
			"txID": "997ce381e04206eac74ab911bc21ded3bc1e6a1dc032d900e9e690b549933333",
			"raw_data_hex": "0a026cc4...",
			"raw_data": {},
			"signature": ["xxxxxxxxx"]
		}
	}`)

	req, _ := http.NewRequest("POST", "https://api.tronsave.io/v2/orders/sell-manual", bytes.NewBuffer(body))
	req.Header.Set("apikey", "YOUR_API_KEY")
	req.Header.Set("content-type", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	out, _ := io.ReadAll(resp.Body)
	fmt.Println(string(out))
}
```

{% endtab %}

{% tab title="Rust" %}

```rust
// Cargo.toml:
// reqwest = { version = "0.12", features = ["blocking", "json"] }
// serde_json = "1"
use serde_json::json;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    // signed_tx must be a JSON object (e.g. produced by a TRON SDK)
    let payload = json!({
        "orderId": "6a0141d98e6f3f91d2284444",
        "paymentAddress": "YOUR_TRON_ADDRESS",
        "isAllowSellForLockedDelegator": false,
        "signedTx": {
            "visible": false,
            "txID": "997ce381e04206eac74ab911bc21ded3bc1e6a1dc032d900e9e690b549933333",
            "raw_data_hex": "0a026cc4...",
            "raw_data": {},
            "signature": ["xxxxxxxxx"]
        }
    });

    let client = reqwest::blocking::Client::new();
    let res = client
        .post("https://api.tronsave.io/v2/orders/sell-manual")
        .header("apikey", "YOUR_API_KEY")
        .header("content-type", "application/json")
        .json(&payload)
        .send()?;

    let body: serde_json::Value = res.json()?;
    println!("{}", body);
    Ok(())
}
```

{% endtab %}
{% endtabs %}

## 端到端示例：构建签名交易

Step 2 中的签名交易是一笔 TRON `DelegateResourceContract`，它将资源从您的钱包（委托方 delegator）代理给订单的 `receiver`（接收地址）。下面的示例使用 TronWeb 获取活跃订单、选取价格最高的订单、推导冻结费率、构建并签名代理交易，然后提交。

{% hint style="info" %}
本示例面向 Nile（测试网）。TronSave 基础 URL `https://api-dev.tronsave.io` 和 TronWeb 全节点 `https://api.nileex.io` 用于测试。对于主网，请使用 `https://api.tronsave.io` 和 `https://api.trongrid.io`。
{% endhint %}

{% tabs %}
{% tab title="JavaScript" %}

```javascript
/**
 * Copy-and-run manual sell flow:
 * 1) Fill the config below
 * 2) node src/test/sellManualFlow.guide.js
 */
const { TronWeb } = require("tronweb");

const API_KEY = "your_api_key"; // change it later, (CONTACT :https://t.me/wantingtrx)
const TRONSAVE_API_URL = "https://api-dev.tronsave.io"; // change it later (api-dev = Nile testnet) or "https://api.tronsave.io" (mainnet)
const PRIVATE_KEY = "your_private_key"; // change it later
const INPUT_RESOURCE_TYPE = "ENERGY"; // change it later ("ENERGY" | "BANDWIDTH")
const PAGE = 0; // change it later
const PAGE_SIZE = 10; // change it later
const PAYMENT_ADDRESS = ""; // optional, change it later
const IS_ALLOW_SELL_FOR_LOCKED_DELEGATOR = false; // change it later
const TRONWEB_FULL_HOST = "https://api.nileex.io"; // change it later if needed (api-dev = Nile testnet) or "https://api.trongrid.io" (mainnet)
const RESOURCE_AMOUNT_OVERRIDE = 0; // optional, 0 means use targetOrder.remainAmount
const TRON_BLOCK_TIME_IN_SECONDS = 3;
const FALLBACK_FREEZE_RATE = 150; // resource per 1 TRX
const ALLOW_FALLBACK_FREEZE_RATE = false; // true only if you intentionally want fallback

const getChainParamValue = (params, key) => {
    const found = params.find((param) => param.key === key);
    return Number((found && found.value) || 0);
};

const getFreezeRateFromAccountAndChainParams = async (tronWeb, delegatorAddress, resourceType) => {
    const account = await tronWeb.trx.getAccount(delegatorAddress);
    if (!account || !account.address) {
        throw new Error("Delegator address is not activated on chain");
    }

    const accountResources = await tronWeb.trx.getAccountResources(delegatorAddress);
    if (resourceType === "ENERGY") {
        const totalLimit = Number((accountResources && accountResources.TotalEnergyLimit) || 0);
        const totalWeight = Number((accountResources && accountResources.TotalEnergyWeight) || 0);
        if (totalLimit > 0 && totalWeight > 0) return totalLimit / totalWeight;
    } else {
        const totalLimit = Number((accountResources && accountResources.TotalNetLimit) || 0);
        const totalWeight = Number((accountResources && accountResources.TotalNetWeight) || 0);
        if (totalLimit > 0 && totalWeight > 0) return totalLimit / totalWeight;
    }

    const chainParams = await tronWeb.trx.getChainParameters();
    if (!Array.isArray(chainParams) || chainParams.length === 0) {
        throw new Error("Cannot load chain parameters");
    }

    if (resourceType === "ENERGY") {
        const totalLimit = getChainParamValue(chainParams, "getTotalEnergyCurrentLimit");
        const totalWeight = getChainParamValue(chainParams, "getTotalEnergyWeight");
        if (totalLimit > 0 && totalWeight > 0) return totalLimit / totalWeight;
    } else {
        const totalLimit = getChainParamValue(chainParams, "getTotalNetLimit");
        const totalWeight = getChainParamValue(chainParams, "getTotalNetWeight");
        if (totalLimit > 0 && totalWeight > 0) return totalLimit / totalWeight;
    }

    throw new Error("Cannot derive freeze rate from chain params");
};

const assertConfig = () => {
    if (!API_KEY || API_KEY === "your_api_key") throw new Error("API_KEY is required");
    if (!PRIVATE_KEY || PRIVATE_KEY === "your_private_key") throw new Error("PRIVATE_KEY is required");
    if (!["ENERGY", "BANDWIDTH"].includes(INPUT_RESOURCE_TYPE)) {
        throw new Error("INPUT_RESOURCE_TYPE must be ENERGY or BANDWIDTH");
    }
};

const requestJson = async (url, init) => {
    const response = await fetch(url, {
        ...init,
        headers: {
            apikey: API_KEY,
            "content-type": "application/json",
            ...(init && init.headers ? init.headers : {}),
        },
    });
    const data = await response.json();
    if (!response.ok || data.error) throw new Error(`Request failed (${response.status}): ${data.message}`);
    return data;
};

const getOrdersActiveGlobal = async () => {
    const url = `${TRONSAVE_API_URL}/v2/orders/active-global?page=${PAGE}&pageSize=${PAGE_SIZE}`;
    const result = await requestJson(url, { method: "GET" });
    return result.data.data;
};

const buildAndSignDelegateTransaction = async (targetOrder) => {
    const tronWeb = new TronWeb({ fullHost: TRONWEB_FULL_HOST });
    const delegatorAddress = tronWeb.address.fromPrivateKey(PRIVATE_KEY);
    if (!delegatorAddress) throw new Error("Invalid PRIVATE_KEY");

    const targetResourceAmount = RESOURCE_AMOUNT_OVERRIDE > 0 ? RESOURCE_AMOUNT_OVERRIDE : targetOrder.remainAmount;
    if (!targetResourceAmount || targetResourceAmount <= 0) {
        throw new Error("Invalid target resource amount. Check target order remainAmount or RESOURCE_AMOUNT_OVERRIDE");
    }

    let freezeRate = FALLBACK_FREEZE_RATE;
    try {
        freezeRate = await getFreezeRateFromAccountAndChainParams(tronWeb, delegatorAddress, INPUT_RESOURCE_TYPE);
    } catch (error) {
        if (!ALLOW_FALLBACK_FREEZE_RATE) {
            throw new Error(
                `Cannot derive freeze rate on node ${TRONWEB_FULL_HOST}: ${error.message}. ` +
                "Check TRONWEB_FULL_HOST matches api-dev network and delegator address is activated."
            );
        }
        console.log(`Cannot derive freeze rate from node, fallback to ${FALLBACK_FREEZE_RATE}`);
    }
    if (!freezeRate || freezeRate <= 0) throw new Error("Invalid freezeRate");

    const delegateTrxAmount = Math.ceil(targetResourceAmount / freezeRate);
    const delegateAmountInSun = delegateTrxAmount * 1000000;
    if (delegateAmountInSun <= 0) throw new Error("Invalid delegateAmountInSun");

    const lockPeriodInBlocks = Math.floor(targetOrder.durationSec / TRON_BLOCK_TIME_IN_SECONDS);
    if (lockPeriodInBlocks <= 0) {
        throw new Error("Invalid lockPeriodInBlocks");
    }
    console.log({ freezeRate, delegateTrxAmount, delegateAmountInSun, lockPeriodInBlocks, tronNode: TRONWEB_FULL_HOST });

    const unsignedTx = await tronWeb.transactionBuilder.delegateResource(
        delegateAmountInSun,
        targetOrder.receiver,
        INPUT_RESOURCE_TYPE,
        delegatorAddress,
        true,
        lockPeriodInBlocks
    );

    return tronWeb.trx.sign(unsignedTx, PRIVATE_KEY);
};

const sellManual = async (orderId, signedTx) => {
    const url = `${TRONSAVE_API_URL}/v2/orders/sell-manual`;
    return requestJson(url, {
        method: "POST",
        body: JSON.stringify({
            orderId,
            signedTx,
            paymentAddress: PAYMENT_ADDRESS || undefined,
            isAllowSellForLockedDelegator: IS_ALLOW_SELL_FOR_LOCKED_DELEGATOR,
        }),
    });
};

const run = async () => {
    assertConfig();

    console.log("Step 1: Get active orders");
    const orders = await getOrdersActiveGlobal();
    const activeOrders = orders.filter(
        (order) => order.status === "Active" && order.remainAmount > 0 && order.resourceType === INPUT_RESOURCE_TYPE
    );
    if (activeOrders.length === 0) throw new Error("No active order found");

    const targetOrder = activeOrders.sort((a, b) => {
        if (b.price !== a.price) return b.price - a.price;
        return b.remainAmount - a.remainAmount;
    })[0];

    console.log("Step 2: Picked highest-price active order", {
        orderId: targetOrder.id,
        receiver: targetOrder.receiver,
        remainAmount: targetOrder.remainAmount,
        price: targetOrder.price,
        resourceType: targetOrder.resourceType,
        durationSec: targetOrder.durationSec,
        inputResourceType: INPUT_RESOURCE_TYPE,
    });

    console.log("Step 3: Build and sign delegate tx with TronWeb");
    const signedTx = await buildAndSignDelegateTransaction(targetOrder);
    console.log({ signedTxId: signedTx.txID || "N/A" });

    console.log("Step 4: Sell manual");
    const result = await sellManual(targetOrder.id, signedTx);
    console.log("Done:", result);
};

run().catch((error) => {
    console.error("Failed:", error.message);
    process.exitCode = 1;
});
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
/**
 * Copy-and-run manual sell flow (PHP):
 * 1) Fill the config below
 * 2) php src/test/sellManualFlow.guide.php
 */

$API_KEY = "your_api_key"; // change it later, (CONTACT :https://t.me/wantingtrx)
$TRONSAVE_API_URL = "https://api-dev.tronsave.io"; // change it later
$INPUT_RESOURCE_TYPE = "ENERGY"; // change it later: ENERGY | BANDWIDTH
$PAGE = 0; // change it later
$PAGE_SIZE = 10; // change it later
$PAYMENT_ADDRESS = ""; // optional, change it later
$IS_ALLOW_SELL_FOR_LOCKED_DELEGATOR = false; // change it later
$SIGNED_TX_JSON = '{"txID":"your_signed_tx_id","raw_data_hex":"your_raw_data_hex"}'; // change it later

function assertConfig(
    string $apiKey,
    string $resourceType,
    string $signedTxJson
): void {
    if ($apiKey === "" || $apiKey === "your_api_key") {
        throw new RuntimeException("API_KEY is required");
    }
    if (!in_array($resourceType, ["ENERGY", "BANDWIDTH"], true)) {
        throw new RuntimeException("INPUT_RESOURCE_TYPE must be ENERGY or BANDWIDTH");
    }
    if (str_contains($signedTxJson, "your_signed_tx_id")) {
        throw new RuntimeException("Please update SIGNED_TX_JSON");
    }
}

function requestJson(string $url, string $apiKey, string $method = "GET", ?array $body = null): array
{
    $headers = [
        "apikey: {$apiKey}",
        "content-type: application/json",
    ];
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    if ($body !== null) {
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body, JSON_UNESCAPED_SLASHES));
    }
    $raw = curl_exec($ch);
    $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
    if ($raw === false) {
        $error = curl_error($ch);
        curl_close($ch);
        throw new RuntimeException("cURL error: {$error}");
    }
    curl_close($ch);

    $data = json_decode($raw, true);
    if (!is_array($data)) {
        throw new RuntimeException("Invalid JSON response: {$raw}");
    }
    if ($httpCode >= 400 || !empty($data["error"])) {
        $message = $data["message"] ?? "Unknown error";
        throw new RuntimeException("Request failed ({$httpCode}): {$message}");
    }
    return $data;
}

function getOrdersActiveGlobal(string $baseUrl, string $apiKey, int $page, int $pageSize): array
{
    $query = http_build_query(["page" => $page, "pageSize" => $pageSize]);
    $url = "{$baseUrl}/v2/orders/active-global?{$query}";
    $result = requestJson($url, $apiKey, "GET");
    return $result["data"]["data"] ?? [];
}

function pickHighestPriceOrder(array $orders, string $resourceType): array
{
    $candidates = array_values(array_filter($orders, function ($order) use ($resourceType) {
        return ($order["status"] ?? "") === "Active"
            && (int)($order["remainAmount"] ?? 0) > 0
            && ($order["resourceType"] ?? "") === $resourceType;
    }));
    if (count($candidates) === 0) {
        throw new RuntimeException("No active order found");
    }

    usort($candidates, function ($a, $b) {
        $priceDiff = (float)($b["price"] ?? 0) <=> (float)($a["price"] ?? 0);
        if ($priceDiff !== 0) return $priceDiff;
        return (int)($b["remainAmount"] ?? 0) <=> (int)($a["remainAmount"] ?? 0);
    });
    return $candidates[0];
}

function sellManual(
    string $baseUrl,
    string $apiKey,
    string $orderId,
    array $signedTx,
    string $paymentAddress,
    bool $allowLockedDelegator
): array {
    $url = "{$baseUrl}/v2/orders/sell-manual";
    $payload = [
        "orderId" => $orderId,
        "signedTx" => $signedTx,
        "paymentAddress" => $paymentAddress !== "" ? $paymentAddress : null,
        "isAllowSellForLockedDelegator" => $allowLockedDelegator,
    ];
    return requestJson($url, $apiKey, "POST", $payload);
}

try {
    assertConfig($API_KEY, $INPUT_RESOURCE_TYPE, $SIGNED_TX_JSON);
    $signedTx = json_decode($SIGNED_TX_JSON, true);
    if (!is_array($signedTx)) {
        throw new RuntimeException("SIGNED_TX_JSON must be valid JSON object");
    }

    echo "Step 1: Get active orders\n";
    $orders = getOrdersActiveGlobal($TRONSAVE_API_URL, $API_KEY, $PAGE, $PAGE_SIZE);

    $targetOrder = pickHighestPriceOrder($orders, $INPUT_RESOURCE_TYPE);
    echo "Step 2: Picked highest-price active order\n";
    print_r([
        "orderId" => $targetOrder["id"] ?? null,
        "receiver" => $targetOrder["receiver"] ?? null,
        "remainAmount" => $targetOrder["remainAmount"] ?? null,
        "price" => $targetOrder["price"] ?? null,
        "resourceType" => $targetOrder["resourceType"] ?? null,
        "durationSec" => $targetOrder["durationSec"] ?? null,
        "inputResourceType" => $INPUT_RESOURCE_TYPE,
    ]);

    echo "Step 3: Use pre-signed delegate tx from SIGNED_TX_JSON\n";
    print_r(["signedTxId" => $signedTx["txID"] ?? "N/A"]);

    echo "Step 4: Sell manual\n";
    $result = sellManual(
        $TRONSAVE_API_URL,
        $API_KEY,
        (string)$targetOrder["id"],
        $signedTx,
        $PAYMENT_ADDRESS,
        $IS_ALLOW_SELL_FOR_LOCKED_DELEGATOR
    );
    echo "Done:\n";
    print_r($result);
} catch (Throwable $e) {
    fwrite(STDERR, "Failed: " . $e->getMessage() . PHP_EOL);
    exit(1);
}
```

{% endtab %}

{% tab title="Python" %}

```python
#!/usr/bin/env python3
"""
Copy-and-run manual sell flow (Python):
1) Fill the config below
2) python3 src/test/sellManualFlow.guide.py
"""

import json
import urllib.error
import urllib.parse
import urllib.request
from typing import Any, Dict, List

API_KEY = "your_api_key"  # change it later, (CONTACT :https://t.me/wantingtrx)
TRONSAVE_API_URL = "https://api-dev.tronsave.io"  # change it later
INPUT_RESOURCE_TYPE = "ENERGY"  # change it later: ENERGY | BANDWIDTH
PAGE = 0  # change it later
PAGE_SIZE = 10  # change it later
PAYMENT_ADDRESS = ""  # optional, change it later
IS_ALLOW_SELL_FOR_LOCKED_DELEGATOR = False  # change it later
SIGNED_TX_JSON = '{"txID":"your_signed_tx_id","raw_data_hex":"your_raw_data_hex"}'  # change it later


def assert_config() -> None:
    if not API_KEY or API_KEY == "your_api_key":
        raise ValueError("API_KEY is required")
    if INPUT_RESOURCE_TYPE not in ("ENERGY", "BANDWIDTH"):
        raise ValueError("INPUT_RESOURCE_TYPE must be ENERGY or BANDWIDTH")
    if "your_signed_tx_id" in SIGNED_TX_JSON:
        raise ValueError("Please update SIGNED_TX_JSON")


def request_json(url: str, method: str = "GET", body: Dict[str, Any] | None = None) -> Dict[str, Any]:
    payload = None
    if body is not None:
        payload = json.dumps(body).encode("utf-8")
    req = urllib.request.Request(
        url=url,
        method=method,
        data=payload,
        headers={
            "apikey": API_KEY,
            "content-type": "application/json",
        },
    )
    try:
        with urllib.request.urlopen(req) as response:
            data = json.loads(response.read().decode("utf-8"))
            if response.status >= 400 or data.get("error"):
                raise RuntimeError(f"Request failed ({response.status}): {data.get('message')}")
            return data
    except urllib.error.HTTPError as e:
        raw = e.read().decode("utf-8", errors="ignore")
        raise RuntimeError(f"HTTPError ({e.code}): {raw}") from e


def get_orders_active_global() -> List[Dict[str, Any]]:
    query = urllib.parse.urlencode({"page": PAGE, "pageSize": PAGE_SIZE})
    url = f"{TRONSAVE_API_URL}/v2/orders/active-global?{query}"
    result = request_json(url, "GET")
    return result["data"]["data"]


def pick_highest_price_order(orders: List[Dict[str, Any]]) -> Dict[str, Any]:
    candidates = [
        o for o in orders
        if o.get("status") == "Active"
        and int(o.get("remainAmount", 0)) > 0
        and o.get("resourceType") == INPUT_RESOURCE_TYPE
    ]
    if not candidates:
        raise RuntimeError("No active order found")
    candidates.sort(key=lambda o: (float(o.get("price", 0)), int(o.get("remainAmount", 0))), reverse=True)
    return candidates[0]


def sell_manual(order_id: str, signed_tx: Dict[str, Any]) -> Dict[str, Any]:
    url = f"{TRONSAVE_API_URL}/v2/orders/sell-manual"
    payload = {
        "orderId": order_id,
        "signedTx": signed_tx,
        "paymentAddress": PAYMENT_ADDRESS or None,
        "isAllowSellForLockedDelegator": IS_ALLOW_SELL_FOR_LOCKED_DELEGATOR,
    }
    return request_json(url, "POST", payload)


def main() -> None:
    assert_config()
    signed_tx = json.loads(SIGNED_TX_JSON)

    print("Step 1: Get active orders")
    orders = get_orders_active_global()

    target = pick_highest_price_order(orders)
    print("Step 2: Picked highest-price active order", {
        "orderId": target.get("id"),
        "receiver": target.get("receiver"),
        "remainAmount": target.get("remainAmount"),
        "price": target.get("price"),
        "resourceType": target.get("resourceType"),
        "durationSec": target.get("durationSec"),
        "inputResourceType": INPUT_RESOURCE_TYPE,
    })

    print("Step 3: Use pre-signed delegate tx from SIGNED_TX_JSON")
    print({"signedTxId": signed_tx.get("txID", "N/A")})

    print("Step 4: Sell manual")
    result = sell_manual(target["id"], signed_tx)
    print("Done:", result)


if __name__ == "__main__":
    main()
```

{% endtab %}
{% endtabs %}

## 后续步骤

* 初次使用 API？请从 [快速开始](/chinese/developers/quickstart.md) 和 [身份验证](/chinese/developers/authentication.md) 开始。
* 了解 [能量与带宽](/chinese/concepts/energy-and-bandwidth.md) 的区别，并查看 [订单类型](/chinese/concepts/order-types.md)。
* 想买入而不是卖出？请参见 [购买资源](/chinese/developers/api-reference/buy-resources.md)。


---

# 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:

```
GET https://docs.tronsave.io/chinese/developers/api-reference/sell-resources.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
