How it Works
- User (Client): Constructs a transaction and calls
.delegate(). This returns a typed object (for UI/validation) and a serialized string (for transport). - Transport: The user sends the serialized string (
payload) to your backend. - Relayer (Server): Your server decodes the payload back into an object, validates it, wraps it, and submits it.
1. Client Side: Signing the Action
On the frontend, use the.delegate() method to sign the queued actions without submitting a transaction or paying gas. It may make RPC calls to look up the nonce and expiration block height, or prompt a connected wallet to sign.
For local signing, set the delegate nonce with .delegate({ nonce: n }). For V2 delegates, use .delegateV2({ nonce: n }), adding nonceIndex when signing with a gas-key slot. The builder’s .nonce(n) method is reserved for the outer transaction: combining it with delegate() or delegateV2() throws before RPC calls or signing, even if a delegate nonce option is also provided. Wallets choose their own delegate nonce.
A relayer can still use .nonce(n).signedDelegateAction(signedDelegateAction).send() (or signedDelegateActionV2) to choose its outer transaction nonce independently of the user’s signed delegate nonce.
It returns two properties:
signedDelegateAction: The raw JS object. Useful for debugging or showing details in your UI.payload: A Base64-encoded string. This is what you send to the server.
2. Server Side: The Relayer
The server receives the Base64 string (payload). It must decode this string back into a typed object to inspect it before submitting.
Use near-kit’s decodeSignedDelegateAction helper for this.
Example: Express.js Route
Security Checklist
Running a relayer makes you a target.- Whitelist Receivers: Always check
userAction.delegateAction.receiverId. - Whitelist Methods: Inspect
userAction.delegateAction.actionsto ensure users are only calling allowed methods (e.g., “move” vs “withdraw”). - Rate Limiting: Rate limit your API endpoint to prevent draining your relayer’s funds.