Skip to main content

Composition

Shugo is one Anchor program that composes with Solana’s Subscriptions & Allowances program rather than reimplementing token custody. The only thing Shugo’s program ever does to a token balance is CPI into S&A’s own transfer_fixed instruction — it never moves a token directly. The first three calls can be bundled into a single transaction — this is exactly what the CLI’s create-policy command and the SDK’s createPolicyAndDelegation do.

The Policy account

One Policy PDA exists per (human, mint, policy_id) triple. Its own address is what gets registered as delegatee on the real S&A delegation — that registration is the entire mechanism. From that point on, S&A will only accept a transfer_fixed call where the signer is this exact address, and the only way to produce that signature is through Shugo’s own execute_pull, which runs the policy checks first. PDA seeds: ["policy", human, mint, policy_id.to_le_bytes()].

Instructions

Why the destination check lives in Shugo, not S&A

S&A’s own transfer_fixed takes a receiver token account as a plain argument with no constraint tying it to anything set when the delegation was created — by design, S&A caps how much and until when, not where to. A delegatee can redirect a pull to any token account of the right mint. Shugo’s execute_pull adds exactly the check that’s missing: the requested destination must be one of the policy’s up-to-four allowlisted accounts, checked before the CPI ever happens, independent of anything S&A itself validates.

Deployed addresses (devnet)

What’s deliberately out of scope today

Two gaps are real and intentional, not oversights — see the FAQ for the full reasoning. execute_pull does not yet forward Token-2022 transfer-hook accounts, so hook-enabled mints aren’t supported yet. init_policy does not yet validate that a mint is one Shugo can actually handle. Both are scoped, both have a clear extension point already built into the SDK (resolveExtraAccountsForMint and checkMintSupported in @shugo/sdk), and neither is a correctness or security gap in what’s already shipped — a pull against an unsupported mint fails cleanly at the S&A CPI boundary rather than misbehaving.