Wallet
On this page
The Gully Game SDK never holds a private key. Every write to Gully Chain (submitting a score, completing a quest, paying an x402 invoice) goes through one interface, ITransactionSender, that you implement with whatever account the player uses.
#The seam
namespace GullyLabs.GameSdk
{
public sealed class TransactionRequest
{
public string To;
public string Data = "0x";
public BigInteger Value = BigInteger.Zero;
public long ChainId = GullyChain.TestnetChainId; // 485590
}
public interface ITransactionSender
{
string Address { get; }
Task<string> SendTransactionAsync(TransactionRequest tx); // returns the tx hash once broadcast
}
}SDK methods that write take an ITransactionSender argument, for example GullyArcadeClient.SubmitScoreAsync(attestation, sender) and X402Client.GetAsync(url, sender). Methods that only build calldata (Erc20.TransferCalldata, Abi.Call) let you send it yourself.
#Which account backs it
| Account | When | Notes |
|---|---|---|
| Player's Gully ID smart account | default for Gully Studio games | passkey-owned smart account (ZeroDev Kernel); the game acts through a session key with limited permissions; gas sponsored by the Gully Chain paymaster |
| Injected or external wallet | WebGL builds, power users | a thin bridge to the browser wallet |
| Server-side signer | dedicated-server games that settle results themselves | the key lives on the game server under Gully infrastructure rules, never in the client build |
The player's address comes from the Gully ID gully_account claim (see Gully ID).
#Session keys
A passkey cannot sign silently mid-game. Instead, at sign-in the player approves a session key: a short-lived key, held by the game client, that the smart account accepts only for the calls and limits you request (for example: only submitScore on GullyArcade and complete on QuestRegistry, for 24 hours, with no value transfers). If the device is lost, the session expires on its own.
Coming soon
The SDK ships the ITransactionSender seam but no built-in Kernel session-key sender yet. Implement the interface against the Gully ID account stack, or use a server-side signer for testnet prototypes.
#Sponsored gas
Gully accounts on Gully Chain have gas sponsored through the ERC-4337 paymaster, so players do not need ETH to play. The sender builds a user operation for the smart account and sends it to the Gully Chain bundler; the paymaster pays the fee.
#A minimal sender for testing
For editor tests against a local chain or a throwaway testnet key, a sender can sign raw transactions and post them with JsonRpcClient.SendRawTransactionAsync. Keep such keys out of the repo and out of builds.
public sealed class DevSender : ITransactionSender
{
readonly JsonRpcClient _rpc;
readonly IMyTestSigner _signer; // your own signing code, editor-only
public DevSender(JsonRpcClient rpc, IMyTestSigner signer) { _rpc = rpc; _signer = signer; }
public string Address => _signer.Address;
public async Task<string> SendTransactionAsync(TransactionRequest tx)
{
string raw = await _signer.SignAsync(tx); // signed, RLP-encoded transaction hex
return await _rpc.SendRawTransactionAsync(raw);
}
}#Rules
Heads up
Never put a private key, mnemonic or signer key in a game build, a repo, PlayerPrefs or a ScriptableObject. Anything shipped in a client can be extracted.
- Request the narrowest session permissions that work, and the shortest lifetime.
- Show the player what a transaction does before the first approval.
- Link to the explorer after a write:
GullyChain.TxUrl(txHash).