# Solmate holder-access registry The first protocol build includes source, its compiled Solana program, an offline validator test and a devnet deployment script. Holder verification is the only right represented. Assets stay in their existing accounts. The program accepts no token accounts, has no admin instruction and calls only the System Program to create its two types of record. The 9,216-byte artifact was tested with signed transactions on an offline Surfpool validator and deployed to public Solana devnet on November 9, 2026. Deployment, wallet-wide grant and individual revocation finalized successfully. Asset scope, renewal, revoke-all, refund accounting and rejection of unauthorized changes also passed the devnet checks. The program is upgradeable and unaudited. [Program](https://explorer.solana.com/address/FX2zKgkTSrmUpQcfNsrahQN7Mer5gfPo6Ec8FLX3SuaR?cluster=devnet) · [Deployment](https://explorer.solana.com/tx/3eCHdec79tqTeM7ZAvcDW45c97cyKSJxUMCDdJKZ2Bog5uNSqnzo3CxeyW6Y5VQk4kfqTKnVe6eswtqVQPXP2dp9?cluster=devnet) · [Grant](https://explorer.solana.com/tx/4e6nQfFsWk57wLqvDJFY7pXvsKvCkW1MQDnZycdgZoz7NZTWLjTmgA6WuwtUHyr4AYA2gkjK3csqXwLMKks8BbKs?cluster=devnet) · [Revocation](https://explorer.solana.com/tx/2evBj5pt8L6usRqeVxr4QAR4FRdQCT8PHTvEXHAxmQ6wDX19nTQ8vW3WJtWvXaNHjDZK3JNMtDEeNfrDRLaqLf6t?cluster=devnet) **SBF SHA-256:** `9fdcd1f09c0910a6318eb8f5fd9f963a622d4453987d8c815da9466cc17163f1`. ## Rebuild and run locally Run these commands from this directory with Python 3.10+. Get [Anza platform-tools v1.58](https://github.com/anza-xyz/platform-tools/releases/tag/v1.58) for your platform, verify the published archive digest and extract it. The compiler and linker in its `llvm/bin` directory target Solana SBF. The build helper fetches missing C headers and the linker script from a [pinned official SDK revision](https://github.com/solana-labs/solana/tree/d9f20e951a06b61e4505da0955228020b96a8915/sdk/sbf/c). The helper can use an already-downloaded SDK via `--sdk`. ```text python build.py --llvm "/path/to/platform-tools/llvm/bin" python -m pip install solders==0.27.1 python test_registry.py --surfpool "/path/to/surfpool" ``` Use [Surfpool v1.7.0](https://github.com/solana-foundation/surfpool/releases/tag/v1.7.0), or the corresponding `surfpool.exe` on Windows. The shipped binary can be tested without installing a compiler. The script starts a fresh offline chain on loopback, funds throwaway keys, deploys the exact SBF bytes via the upgradeable loader, compares ProgramData to the artifact, and submits signed transactions. It shuts down the validator after the tests. Job outputs and downloaded headers are gitignored. For a persistent development session, append `--serve --fund YOUR_TEST_WALLET_PUBLIC_KEY`. This prints a local RPC URL, local genesis hash and program address, and funds only your test wallet on that chain. The validator lasts until you press Ctrl+C. A public web client pinned to devnet must not accept this local genesis. The tests exercise writable/signer and owner restrictions, account funding before initialization, wallet- and asset-scoped creation, renewal, expiry bounds, canonical grant seeds, invalid scope, wrong System Program, independent single revoke, exact deposit refunds, revoke-all, and old-record invalidation after a same-transaction refund. The registry stores permissions independent of ownership; tests for NFT authenticity and wallet sign-in belong to the partner verifier. ## Provision public devnet Use dedicated devnet keys kept outside the site checkout. The payer temporarily holds upgrade authority. Keep it in operator-controlled storage. Create separate payer and program keypair files with `solana-keygen new --outfile PATH`, outside the checkout. The deployer calculates the required balance from current account deposits and a fee allowance. It counts the upload buffer deposit once because the loader returns it when it creates the executable. On November 9, 2026 the fresh deployment and test budget was 0.055772560 devnet SOL. Enter only the public payer address when using a faucet. [Solana devnet faucet](https://faucet.solana.com/) · [ZalalenA devnet faucet](https://faucet.zalalena.com/solana/) ```text python devnet_smoke.py --payer-keypair "/private/test-payer.json" --program-keypair "/private/test-program.json" ``` The deployment script verifies devnet genesis before writing. A successful run prints the program ID, binary hash and test receipts. The deployer verifies the program header, owner, authority and code bytes. A retry against a matching live deployment runs the test suite again. To resume an interrupted upload, pass `--resume-buffer PUBLIC_BUFFER_ADDRESS` to the same command. The deployer verifies the buffer authority, loader, size and any previously uploaded chunks before writing. ## Binary interface, version 1 All integer fields are little-endian. Addresses and the audience hash are raw 32-byte values. For wallet and single-asset grants the audience is SHA-256 of the UTF-8 string `urn:solmate:delegation:all-apps:v1` for all integrated applications, or the canonical HTTPS origin for a single website. For collection grants, prefix that string with `urn:solmate:verified-collection:v1:` before hashing and use scope byte 1 with the collection mint. Verifiers must validate current NFT ownership and verified collection membership before recognizing that grant for an NFT. Each app binds sign-in to its own trusted origin. All existing HTTPS-scoped records keep their original meaning. Scope byte `0` means supported holdings in the vault and requires an all-zero asset ID. Scope byte `1` means exactly the specified nonzero asset address. The delegate must differ from the vault and the zero address. | Instruction | Data fields in order | Account metas in order | | --- | --- | --- | | Grant or renew | `u8 0, scope:u8, delegate:32, audience:32, asset:32, expiresAt:i64` (106 bytes) | vault signer+writable, state writable, grant writable, System Program | | Revoke one | `u8 1` | vault signer+writable, grant writable | | Revoke all | `u8 2` | vault signer, state writable | Only ordinary system-owned signing wallets with empty data can authorize these operations. Use the chain clock and an expiry greater than now and at most 366 days away. Creating the first grant creates a persistent vault-state record and a grant record. The vault pays deposits and transaction fees. Revoking an individual grant closes its account and returns its deposit to the vault. Revoke-all increments the persistent epoch; it does not close individual records. An old record may subsequently be closed for its deposit or explicitly reauthorized in the new epoch. The state has no close instruction. | Record | Canonical PDA seeds under the registry program ID | Binary layout (offset) | | --- | --- | --- | | Vault state | UTF-8 `vault`, vault key | type/version (0, value 1); vault (1); u64 epoch (33). Total 41 bytes. | | Grant | UTF-8 `grant`, vault, delegate, audience, one scope byte, asset | type/version (0, value 2); vault (1); delegate (33); audience (65); scope (97); asset (98); u64 epoch (130); i64 expiry (138). Total 146 bytes. | Use the canonical bump from `findProgramAddress`. Verify the producing program ID, network, PDA, type/version, data size, addresses, audience and asset scope at a consistent finalized context. A grant is active only when its stored epoch equals the vault state's epoch and chain time is strictly before expiry. Closing an individual grant ends that grant. Other explicit overlapping grants remain independently valid. The wallet-wide scope covers current and future supported holdings. Revoke-all invalidates all existing scopes. A verified grant lets a partner authenticate the delegate for its named holder feature. The partner must separately verify the everyday wallet's fresh domain-bound signature and the vault's current ownership of the relevant NFT under that asset's actual program. Never grant access to vault account recovery, payments or ownership-changing operations from this record.