Bài viết
Chia sẻ kiến thức của bạn.
+15
Giao dịch Sui thất bại: Đối tượng được dành riêng cho giao dịch khác
Tôi gặp phải một sự kiên trì JsonRpcError
khi cố gắng thực hiện giao dịch trên Sui. Lỗi chỉ ra rằng các đối tượng được dành riêng cho một giao dịch khác, mặc dù tôi đã thực hiện xử lý giao dịch tuần tự với độ trễ.
JsonRpcError: Failed to sign transaction by a quorum of validators because one or more of its objects is reserved for another transaction. Other transactions locking these objects:
- AV7coSQHWg5vN3S47xada6UiZGW54xxUNhRv1QUPqWK (stake 33.83)
- 0x1c20f15cbe780ee7586a2df90c1ab70861ca77a15970bea8702a8cf97bd3eed9
- 0x1c20f15cbe780ee7586a2df90c1ab70861ca77a15970bea8702a8cf97bd3eed9
- 0x1c20f15cbe780ee7586a2df90c1ab70861ca77a15970bea8702a8cf97bd3eed9
Tôi đã thử:
- Thực hiện giao dịch tuần tự (chờ giao dịch trước hoàn thành)
- Thêm độ trễ 3 giây giữa các giao dịch
Và vẫn gặp phải lỗi tương tự một cách nhất quán.
Sử dụng Sui RPC để gửi giao dịch. Cùng một ID đối tượng xuất hiện nhiều lần trong danh sách khóa. Lỗi xảy ra ngay cả với trình tự giao dịch cẩn thận.
- Điều gì khiến các đối tượng bị “dành riêng” cho các giao dịch khác?
- Làm thế nào tôi có thể kiểm tra chính xác xem một đối tượng có sẵn hay không trước khi sử dụng nó trong một giao dịch?
- Có những phương pháp hay nhất để xử lý khóa đối tượng ở Sui không?
- Điều này có liên quan đến thời điểm cuối cùng của giao dịch không?
Có ai gặp phải vấn đề này trước đây không? Bất kỳ thông tin chi tiết nào về quản lý đối tượng phù hợp trong giao dịch Sui sẽ được đánh giá cao!
- Sui
- Transaction Processing
- Move
Câu trả lời
10Lỗi này xảy ra nếu bạn thử và chạy hai giao dịch cùng một lúc (ví dụ: bắt đầu một giao dịch trước khi giao dịch trước kết thúc). Nếu bạn thử lại chạy giao dịch xuất bản mà không chạy giao dịch khác trước hoặc cùng một lúc, giao dịch sẽ thành công. Bạn cũng có thể cần phải lấy thêm khí từ vòi (hoặc đợi một ngày - để kỷ nguyên lật lại - để các vật thể được mở khóa)
Khi bạn chạy một giao dịch liên quan đến các đối tượng mà địa chỉ của bạn sở hữu (như các đối tượng gas), các trình xác thực sẽ giữ lại phiên bản mới nhất của đối tượng được sử dụng bởi giao dịch mà nó đang ký. Nếu bạn thử và chạy hai giao dịch đồng thời và chúng đề cập đến cùng một đối tượng, thì chúng sẽ cạnh tranh với nhau để lấy chữ ký từ trình xác thực. Trong trường hợp hạnh phúc, một trong các giao dịch thắng và chạy, và giao dịch kia không nhận được đủ chữ ký. Trong trường hợp không hài lòng, cả hai giao dịch có thể không nhận đủ chữ ký (nếu cả hai đều nhận được hơn một phần ba chữ ký của trình xác thực, thì cả hai giao dịch đều không thể nhận được hơn hai phần ba, đó là ngưỡng), điều này được gọi là không xác định và từ thời điểm đó các đối tượng là đầu vào cho cả hai giao dịch không thể được sử dụng cho bất kỳ giao dịch nào khác.
Vào cuối kỷ nguyên (chúng kéo dài khoảng một ngày - bạn có thể kiểm tra tiến trình sang thay đổi kỷ nguyên tiếp theo trên https://suiexplorer.com), tất cả các khóa được giải phóng, vì vậy bạn có thể sử dụng lại các đối tượng, nhưng nếu bạn không có thay đổi kỷ nguyên kể từ lần cuối bạn thử, bạn sẽ cần phải thu được nhiều khí hơn.
Này, bạn đang cố gắng thực hiện một giao dịch quá nhanh và các đối tượng bị khóa.
Cố gắng gửi một giao dịch với cùng một đối tượng tại một thời điểm, nếu bạn gửi hai giao dịch, một số trình xác thực có thể chấp nhận giao dịch đầu tiên, một số có thể chấp nhận giao dịch thứ hai và đối tượng của bạn sẽ bị khóa vì mỗi giao dịch cần 66,7% trình xác thực và bạn có thể chỉ nhận được 50%.
=> chỉ cần chờ thiết lập lại kỷ nguyên, nó sẽ sớm
Kiểm tra thêm: https://forums.sui.io/t/beginner-tutorial-error-when-deploying-simple-sui-package/44842
Sui sử dụng**kiểm soát đồng thời lạc quan, nghĩa là các đối tượng bị khóa khi được sử dụng trong giao dịch cho đến khi giao dịch đó được hoàn tất hoặc hết hạn.
Ngay cả khi bạn đợi 3 giây giữa các giao dịch, nếu giao dịch trước đó chưa được hoàn tất, đối tượng vẫn bị khóa. Điều này có nghĩa là giao dịch vẫn đang chờ xử lý và giữ quyền truy cập độc quyền vào đối tượng.
Cách kiểm tra xem một đối tượng có sẵn không
Sử dụng phương pháp Sui RPC:
sui_getObject
Kiểm tra phản hồi cho "status": "Locked"
hoặc"owner": "locked"
.
Yêu cầu ví dụ:
{
"jsonrpc": "2.0",
"id": 1,
"method": "sui_getObject",
"params": ["0x...object_id..."]
}
Nếu bị khóa, chỉ cần đợi thêm và thử lại sau.
Lỗi có nghĩa là các đối tượng giao dịch của bạn sử dụng vẫn bị khóa bởi các giao dịch trước đó chưa hoàn tất. Ngay cả với sự chậm trễ, các đối tượng vẫn được bảo lưu cho đến khi các giao dịch đó hoàn thành trên chuỗi.
Để sửa chữa:
- Luôn xác nhận các giao dịch trước đó liên quan đến các đối tượng đã hoàn tất đầy đủ trước khi sử dụng lại chúng.
- Kiểm tra trạng thái đối tượng thông qua Sui RPC để đảm bảo chúng đã được mở khóa.
- Tránh gửi các giao dịch song song hoặc nhanh chóng trên cùng một đối tượng.
- Thực hiện các lần thử lại với kiểm tra lùi và kiểm tra tính cuối cùng thay vì độ trễ cố định.
Việc khóa này ngăn chặn các cập nhật mâu thuẫn và là bình thường trong mô hình đối tượng của Sui. JsonRpcError
Trình tự thích hợp và xác nhận tính cuối cùng là chìa khóa để tránh.
Lỗi này có nghĩa là các đối tượng bạn đang sử dụng vẫn bị khóa bởi các giao dịch trước đó chưa hoàn tất. Ngay cả với sự chậm trễ, Sui vẫn giữ chúng bảo lưu cho đến khi chuỗi xác nhận hoàn thành.
Để khắc phục nó, hãy đảm bảo mọi giao dịch trước đó sử dụng các đối tượng đó được hoàn tất đầy đủ trước khi sử dụng lại chúng. Bạn có thể kiểm tra trạng thái của họ thông qua Sui RPC để xem chúng có được mở khóa hay không. Tránh gửi nhiều giao dịch hoặc nhanh chóng liên quan đến cùng một đối tượng. Thay vì dựa vào độ trễ cố định, hãy sử dụng thử lại với phản hồi và xác nhận tính cuối cùng trước khi thử lại.
Khóa này là một phần của cách Sui đảm bảo cập nhật an toàn, vì vậy việc sắp xếp thứ tự và kiểm tra tính cuối cùng thích hợp là cách để tránh JSONRPCError
That’s a great debugging question — I’ve run into this exact issue before when building staking flows on Sui. Let me answer in detail, step-by-step, from my perspective using Sui daily.
🔍 Why objects get “reserved” in Sui
In Sui, every transaction consumes specific object versions. Once you submit a transaction that references an object, the network puts a lock on that object version until the transaction is finalized (committed or fails).
So when you see:
JsonRpcError: Failed to sign transaction ... one or more of its objects is reserved
it means:
- The object version you’re trying to use is still “in flight” (reserved by another PTB),
- Or you’re re-using an outdated version without refreshing from the fullnode,
- Or the validator quorum hasn’t yet reached finality for the first transaction.
That’s why the same objectId
shows multiple times in the lock list — you’re trying to consume the same locked version repeatedly.
✅ How I check object availability before using
I always follow these steps:
-
After each transaction, re-fetch fresh object refs before building the next transaction block.
const fresh = await provider.getObject({ id: "0x1c20f15cbe780ee...", options: { showOwner: true, showPreviousTransaction: true, showContent: true }, }); const version = fresh.data.version; // must use this version in the next PTB
-
Wait for local execution / finality when submitting. Always request
WaitForLocalExecution
in yoursignAndExecuteTransactionBlock
. This ensures the object is unlocked before you try to use it again.const res = await signer.signAndExecuteTransactionBlock({ transactionBlock: tx, requestType: "WaitForLocalExecution", });
-
Implement a retry with exponential backoff when conflicts occur. Even with sequencing, there are cases where the fullnode lags. I use a
submitWithRetry
wrapper (like the one I showed earlier) that catchesObject version mismatch
and retries after re-fetching the object.
⚡ Best practices for handling object locks
- Never reuse old object references. Always fetch the latest state from the RPC before constructing each PTB.
- Design with parallelism in mind. If multiple independent flows need the same object, shard state across multiple owned objects (avoid a single global shared object).
- Batch operations when possible. Use a single
TransactionBlock
with multiple Move calls instead of sending sequential transactions that lock the same object repeatedly. - Add backoff + retries. I usually back off
100ms, 200ms, 400ms, 800ms … up to ~3s
before giving up. - Use checkpoints/finality awareness. Remember: an object isn’t truly free until the checkpoint is committed.
⏱️ Relation to finality timing
Yes, this is directly related. Even after a transaction appears “executed”, the object may remain reserved until:
- The local fullnode confirms execution,
- The consensus checkpoint includes it,
- The version updates propagate across validators.
That’s why adding a blind 3-second delay
isn’t reliable — network conditions and validator lag can extend this. The right way is fetch-after-execution instead of sleeping.
🛠️ Example: Safe sequential staking
Here’s how I do it in code (simplified):
async function safeStake(signer, validatorAddr, stakeAmountMist, stakeObjectId) {
let attempt = 0;
while (attempt < 5) {
try {
// 1. Fetch latest object version
const obj = await provider.getObject({ id: stakeObjectId, options: { showOwner: true } });
const version = obj.data.version;
// 2. Build PTB with fresh version
const tx = new TransactionBlock();
tx.moveCall({
target: "0x2::staking::request_add_stake",
arguments: [tx.pure(validatorAddr), tx.pure(stakeAmountMist.toString())],
typeArguments: [],
});
// 3. Execute with finality wait
const res = await signer.signAndExecuteTransactionBlock({
transactionBlock: tx,
requestType: "WaitForLocalExecution",
});
console.log("✅ success:", res.digest);
return res;
} catch (e) {
if (e.message.includes("reserved") || e.message.includes("Object version mismatch")) {
attempt++;
const backoff = 200 * 2 ** attempt;
console.warn(`conflict, retrying in ${backoff}ms`);
await new Promise(r => setTimeout(r, backoff));
continue;
}
throw e;
}
}
throw new Error("failed after retries");
}
This ensures:
- I always use the latest object version,
- I wait for finality,
- I retry on transient lock errors.
🧩 TL;DR
- Cause: Object locks happen because an object version is still reserved by an in-flight transaction.
- Fix: Always re-fetch latest object versions, wait for local execution, and retry on conflicts.
- Best practice: Shard state, batch operations, and use retries/backoff rather than fixed sleeps.
- Yes, finality timing matters. Blind delays won’t solve it — confirmation checks + re-fetching will.
Bạn có biết câu trả lời không?
Hãy đăng nhập và chia sẻ nó.
Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.
Kiếm phần của bạn từ 1000 Sui
Tích lũy điểm danh tiếng và nhận phần thưởng khi giúp cộng đồng Sui phát triển.
- Cách tối đa hóa lợi nhuận nắm giữ SUI: Sui Staking vs Liquid Staking615
- Tại sao BCS yêu cầu thứ tự trường chính xác để khử chuỗi khi cấu trúc Move có các trường được đặt tên?65
- Nhiều lỗi xác minh nguồn” trong các ấn phẩm về mô-đun Sui Move - Giải quyết lỗi tự động55
- Lỗi Sui Move - Không thể xử lý giao dịch Không tìm thấy đồng xu gas hợp lệ cho giao dịch419
- Giao dịch Sui thất bại: Đối tượng được dành riêng cho giao dịch khác410