Trong cộng đồng người dùng tiền mã hóa, có một niềm tin phổ biến: nếu bạn nhập đúng địa chỉ, chọn đúng mạng và có đủ token gốc để trả phí, giao dịch sẽ thành công. Trong đại đa số trường hợp, niềm tin đó đúng.
Nhưng mỗi blockchain đều có những quy tắc ẩn — những quy tắc chỉ lộ ra trong những tình huống rất cụ thể. Và khi điều đó xảy ra, một giao dịch có thể thất bại mặc dù, theo quan điểm của bạn, mọi thứ đều được thực hiện chính xác.
Đó chính xác là chuyện đã xảy ra với chúng tôi khi một khách hàng của dịch vụ hoán đổi của chúng tôi, Rabbit.io, hoán đổi LTC sang SOL. Số tiền rất nhỏ — chưa đến 0.0003 SOL, tính theo giá hiện tại là dưới ba xu. Trong nhiều bài viết trước của tôi, tôi đã đề cập rằng chúng tôi không áp đặt giới hạn tối thiểu cho các lệnh hoán đổi. Đúng vậy, chúng tôi thường xử lý cả những khoản cực kỳ nhỏ.
Khi chúng tôi cố gửi số SOL theo yêu cầu, chúng tôi gặp vấn đề. Địa chỉ là chính xác. Mạng là Solana Mainnet. Số dư của chúng tôi đủ. Phí đã được tính toán. Nhưng giao dịch vẫn cứ thất bại, lặp đi lặp lại.
Lời giải thích hóa ra là bất ngờ — ngay cả với chúng tôi. Và khi chúng tôi chia sẻ điều đó với khách hàng, ban đầu anh ta cũng không tin. Sự cố khiến tôi tự hỏi: còn bao nhiêu cạm bẫy ẩn tương tự tồn tại trên các blockchain khác?
Tôi quyết định điều tra và tổng hợp những ví dụ thú vị nhất — những trường hợp có thể khiến ngay cả tôi cũng bị bất ngờ, mặc dù tôi xử lý rất nhiều loại trao đổi tiền mã hóa hàng ngày.
Nhiều người đã nghe rằng trên Solana, để nhận các token SPL — như USDT, USDC và các token khác — tại một địa chỉ chưa từng giữ những token đó trước đây, địa chỉ cần có một lượng SOL nhỏ. Nói chính xác thì điều đó không hoàn toàn đúng, nhưng có phần sự thật.
Mỗi token được lưu trữ trong một Associated Token Account (ATA) riêng, và việc tạo tài khoản đó tốn khoảng 0.002 SOL. Tuy nhiên, ngay cả khi người nhận có 0 SOL, vẫn có thể nhận token — bởi vì người gửi sẽ trả chi phí tạo ATA, chứ không phải người nhận.
Điều ít được biết hơn là yêu cầu tương tự đôi khi có thể áp dụng cho các chuyển khoản SOL gốc. Đó chính xác là chuyện đã xảy ra với chúng tôi khi chúng tôi cố gửi khoản rất nhỏ mà khách hàng yêu cầu.
Trên Solana, một account không chỉ là một chuỗi ký tự biểu thị địa chỉ. Nó là một bản ghi thực sự được lưu trong trạng thái hiện tại của blockchain. Bản ghi đó chiếm dung lượng đĩa trên các node validator — và lưu trữ không phải là miễn phí.
Cơ chế này được gọi là miễn thuê (rent exemption). Để một account tồn tại, số dư của nó phải vượt quá một ngưỡng tối thiểu — về thực chất là chi phí của hai năm tiền thuê lưu trữ.
Giá trị này không cố định mãi mãi, nhưng hiện tại, đối với một hệ thống account cơ bản (ví thông thường), mức tối thiểu là 890.880 lamports, hay 0.00089088 SOL.
Nếu một địa chỉ chưa tồn tại trên chuỗi (tức là số dư của nó bằng không), giao dịch đầu tiên cố tạo account với một số tiền dưới ngưỡng này sẽ bị từ chối.
Đó chính xác là điều đang xảy ra trong trường hợp của chúng tôi. Chúng tôi gửi một ít hơn khoảng 0.0003 SOL tới một địa chỉ hoàn toàn mới — khoảng ba lần dưới ngưỡng yêu cầu. Khách hàng của chúng tôi từ chối tin rằng luật như vậy tồn tại. Và công bằng mà nói, chúng tôi cũng khó tìm thấy tuyên bố rõ ràng về điều này trong tài liệu chính thức của Solana. Xác nhận gián tiếp duy nhất mà chúng tôi tìm thấy nằm trong mô tả phương thức RPC getMinimumBalanceForRentExemption, ám chỉ quy tắc này — dù nó không nêu đích danh.
Có một điểm quan trọng ở đây. Trước đó chúng tôi đã gửi những khoản rất nhỏ như vậy cho khách hàng nhiều lần mà không gặp vấn đề gì. Từ kinh nghiệm thực tế, chúng tôi biết Solana cho phép chuyển các khoản tiền nhỏ tùy ý.
Và điều đó là đúng — nhưng chỉ khi account người nhận đã tồn tại. Nếu đó là giao dịch đến thứ hai, thứ ba hay thứ trăm, địa chỉ có thể nhận thậm chí 1 lamport. Nhưng khi một account được tạo lần đầu tiên, kiểm tra miễn thuê sẽ được áp dụng.
Vấn đề chúng tôi gặp trên Solana thực ra là một trường hợp cụ thể của một mô hình rộng hơn. Trong vài mạng, địa chỉ như một đối tượng mật mã và account như một mục nhập trong sổ cái phân tán là hai thứ khác nhau. Nếu không có số dư tối thiểu ban đầu, account đơn giản là không tồn tại.
Trên XRP Ledger, mỗi account mới phải nhận một khoản dự trữ tối thiểu là 1 XRP. Trong những ngày đầu của mạng, yêu cầu này cao hơn nhiều — 200 XRP khi ra mắt, sau đó giảm xuống 50 XRP, rồi 20 XRP, rồi 10 XRP. Khi giá XRP tăng theo thời gian, khoản dự trữ dần được hạ xuống đến mức hiện tại là 1 XRP.
Khoản dự trữ này bị khóa vĩnh viễn trong account. Nó không thể được chi tiêu. Nó không thể dùng để trả phí giao dịch. Một số trình khám phá XRP Ledger thậm chí hiển thị tổng số dư và khoản dự trữ bị khóa riêng biệt, để chủ ví có thể thấy rõ bao nhiêu XRP thực sự có thể chi tiêu.
Mạng Stellar theo logic tương tự. Một địa chỉ không tồn tại trong sổ cái cho đến khi nó nhận được số dư tối thiểu. Một account Stellar cần ít nhất 1 XLM để kích hoạt và giữ hoạt động.
Có thể bạn còn nhớ tôi đã từng viết về một lỗi đã biến 1.000 USDC thành 1 XLM. Tình huống mô tả trong bài đó chính là ví dụ của cơ chế này: một giao dịch token, ngay cả khi được ký đúng bởi người gửi, không thể được ghi có cho người nhận trừ khi một số điều kiện tiên quyết được thỏa mãn.
Một đặc điểm nổi bật của cả XRP Ledger và Stellar là một account không thể nhận token trừ khi nó đã rõ ràng đồng ý tương tác với tài sản cụ thể đó. Đây là một quyết định thiết kế có chủ ý của các nhà phát triển, và nó có những lợi thế riêng.
Trên các mạng khác, các địa chỉ của cá voi và người nổi tiếng thường bị tràn ngập token rác mới tạo, tạo ảo giác rằng chủ sở hữu địa chỉ đã mua chúng. Trên XRP Ledger và Stellar, spam kiểu đó là không thể — vì để nhận một tài sản đòi hỏi sự ủy quyền trước.
Lightning Network là một mạng lưới kênh thanh toán được thiết kế để gửi bitcoin mà không ghi lại mọi chuyển khoản lên blockchain. Khác với giao dịch on-chain thông thường, một khoản thanh toán Lightning không được tất cả node trong mạng xác nhận và cũng không được miners xác nhận trong khối. Thay vào đó, nó đi qua một chuỗi các node trung gian.
Và chính ở đây xuất hiện cả một lớp lỗi không rõ ràng.
Để một khoản thanh toán đi từ Alice đến David qua các node trung gian Bob và Carol, mỗi đoạn của tuyến đường phải có đủ thanh khoản theo hướng cần thiết.
Mạng biết tổng dung lượng của mỗi kênh, nhưng nó không biết số dư được phân bổ như thế nào giữa hai bên. Điều này có thể dẫn đến những tình huống như sau:

Kết quả là, khoản thanh toán từ Alice đến David thất bại với lỗi "route not found" — dù Alice có đủ tiền, David có một kênh hoạt động có khả năng nhận thanh toán, và về mặt đồ thị mạng thì một tuyến đường tồn tại.
Còn có một điểm tinh vi khác. Trong LND, một trong những triển khai node Lightning được dùng rộng rãi nhất, giới hạn mặc định cho phí định tuyến được đặt là 0 satoshi.
Điều này có nghĩa node chỉ cố tìm các tuyến hoàn toàn miễn phí — cái mà, trong một mạng thực tế, gần như không tồn tại. Người dùng chưa từng thay đổi thiết lập mặc định này có thể liên tục thấy lỗi "route not found", dù giao dịch sẽ thành công nếu họ cho phép một khoản phí định tuyến nhỏ.
Đây không phải là một lỗi. Trái lại, các nhà phát triển LND cố ý không quyết định thay người dùng mức phí tối đa họ sẵn sàng trả. Nếu một giới hạn dương được đặt mặc định, người dùng sau này có thể phát hiện rằng phí định tuyến đã bị trừ mà họ không hay biết.
Các nguyên nhân khác dẫn đến lỗi "route not found":
Chúng tôi gặp tất cả những vấn đề này khi trao đổi bitcoin qua Lightning Network. Chúng có vẻ khác lạ so với các giao dịch blockchain truyền thống, nhưng mỗi vấn đề đều có giải pháp. Đó là lý do tại Rabbit.io, bạn có thể gửi và nhận bitcoin qua Lightning Network mà không phải lo lắng về những cạm bẫy này.
USDT và USDC là các stablecoin được quản lý tập trung. Hợp đồng thông minh của chúng bao gồm các chức năng danh sách đen: những nhà phát hành — Tether và Circle — có thể thêm một địa chỉ vào danh sách đen bất kỳ lúc nào, sau đó các thao tác liên quan đến token trên địa chỉ đó trở nên không thể thực hiện được.
Đây là một thực tế đã được biết. Điều ít được hiểu rộng rãi hơn là cơ chế hạn chế trong USDT và USDC về bản chất khác nhau — và sự khác biệt đó có hậu quả thực tiễn quan trọng.
Trong hợp đồng USDC, kiểm tra danh sách đen áp dụng cho cả người gửi và người nhận. Nếu địa chỉ người nhận bị đưa vào danh sách đen, giao dịch sẽ bị hợp đồng thông minh từ chối. Người gửi chỉ đơn giản là mất một ít gas, trong khi USDC vẫn ở trong ví họ.
Từ góc nhìn người gửi, đây là mô hình an toàn hơn: tiền không biến mất vào một địa chỉ bị đóng băng.
Trong hợp đồng USDT (TetherToken), hàm transfer chứa kiểm tra sau: require(!isBlackListed[msg.sender]). Điều này có nghĩa chỉ người gửi được kiểm tra đối chiếu với danh sách đen. Địa chỉ người nhận không được xác thực trong quá trình transfer.
Trên thực tế, điều này dẫn đến các hệ quả sau:
Không ai — cả người nhận lẫn người khác — sẽ có thể di chuyển những token đó về sau — các giao dịch đi ra từ một địa chỉ bị đóng băng bị cấm. Chỉ có nhà phát hành, Tether, mới can thiệp được, dùng hàm destroyBlackFunds để thực sự xóa token khỏi địa chỉ bị liệt vào danh sách đen.
Trong trường hợp cụ thể này, có thể tranh luận rằng tốt hơn là một giao dịch được cấu trúc đúng nên bị từ chối ngay lập tức. Cách tiếp cận được thực hiện trong hợp đồng USDC có vẻ minh bạch và công bằng hơn so với mô hình USDT, nơi giao dịch thành công — nhưng sau đó mới rõ rằng chuyển khoản thực tế là vô nghĩa.
Khuyến nghị của tôi:
Nói vậy, ngay cả các chuyển USDT đôi khi vẫn có thể thất bại ngay lập tức — ngay cả khi mọi dữ liệu giao dịch có vẻ đúng. Trong Phần II của bài viết này, tôi sẽ đề cập đến những trường hợp như vậy, cùng các giới hạn giao dịch ít được biết đến hơn trên mạng Bitcoin, Ethereum và Tron.
Nó sẽ được xuất bản tại đây đúng một tuần nữa.