The Android client for the Open Code Protocol (OCP) — the gRPC backend that
handles money movement: transactions/intents, swaps, exchange rates, accounts, and
the Solana transaction construction that backs them. This is the deeper of the two
service modules; services/flipcash wraps the Flipcash app backend and depends on
this one.
Namespace
com.getcode.opencode.*. For the layering this module follows (API → Service → Repository → Controller) see 04 — Networking; for the currency model (USDF, launchpad tokens,AccountCluster) see 06 — Payments & operations.
graph TD
Feature["feature / shared coordinator"]
Ctrl["controllers/* (public, stateful)"]
Repo["repositories/* (interfaces)"]
Svc["internal/network/services/*"]
Exec["internal/network/executors/* (bidi)"]
Api["internal/network/api/* (gRPC stubs)"]
Intents["solana/intents/* + internal/solana/programs/*"]
Backend["OCP backend + Solana"]
Feature --> Ctrl --> Repo --> Svc --> Api --> Backend
Svc --> Exec --> Api
Exec --> Intents
Features consume controllers (and the Exchange interface); everything under
internal/ is off-limits.
| Controller | Purpose | Key surface |
|---|---|---|
TransactionController |
Submits intents (transfer, withdraw, remote send/receive, buy/sell), tracks limits | submitIntent(...), buy/sell/withdraw, limits: StateFlow, implements TransactionOperations |
AccountController |
Account clusters, linking/unlinking, account-state polling | active-cluster + account-list StateFlows |
TokenController |
Stateless network gateway for token metadata / balances | flows only; caching is the consumer's job (the TokenCoordinator) |
CurrencyController |
Rates, currencies, live/historical mint data | streamLiveMintData(), rate flows |
MessagingController |
P2P bill messaging streams (give/grab/claim) | open/poll/ack message stream |
Repositories (repositories/) are the interface seam beneath the controllers:
Transaction, Account, Currency, Messaging, Swap, Event — each implemented
by an Internal*Repository under internal/domain/repositories.
Public domain models live under model/ (see
06 for the economic model):
model/financial/—MintMetadata(aliasToken;Token.usdf),LaunchpadMetadata(USDF-backed bonding-curve fields),Fiat/LocalFiat/VerifiedFiat,Rate,Currency/CurrencyCode,Fee,Limits,Distribution.model/accounts/—AccountCluster(authority + timelock + per-token deposit addresses),TimelockDerivedAccounts,AccountType,GiftCardAccount,PoolAccount.model/transactions/—TransactionMetadata,SwapMetadata/SwapState,ExchangeData(Verified/Unverified).model/core/—OpenCodePayload(the QR/rendezvous payload),ID.
inject/ provides three Hilt modules into SingletonComponent:
OpenCodeModule—Exchange,ProtocolConfig(@OpenCodeProtocol), and the two gRPC channels:@OpenCodeManagedChannel(unary) and@OpenCodeManagedStreamingChannel(keep-alive for streams), both built withAndroidChannelBuilder/OkHttpChannelBuilderand theLoggingClientInterceptor.TransactorModule— transactors +PayloadFactory,AccountClusterFactory.SessionListenersModule— theSessionListenerset (login/logout hooks).
ed25519.shadowplugin: the build appliesflipcash.android.ed25519.shadow, which shades the native Ed25519 library so this module's crypto can't collide with other consumers' versions. It's the only module that needs it.
Money movement is a bidirectional stream, not a fire-and-forget RPC. The client
builds and signs the Solana transactions locally using server-provided parameters.
IntentExecutor (internal/network/executors/) drives it over a
BidirectionalStreamReference (internal/bidi/):
sequenceDiagram
participant C as Client (IntentExecutor)
participant S as OCP server
C->>S: SubmitActions (intent id, actions, signature)
S->>C: ServerParameters (per-action nonces, params)
Note over C: apply params, build txns, sign locally
C->>S: SubmitSignatures
S->>C: Success (or Error)
On Error, SubmitIntentError.typed(...) maps the code to a typed failure; the
InternalTransactionRepository retries on StaleState race conditions
(retryableOrThrow, see 14 — Error handling).
An intent is an IntentType (solana/intents/) holding an ActionGroup of
ActionTypes. Kinds (internal/network/api/intents/): IntentTransfer,
IntentWithdraw, IntentRemoteSend/IntentRemoteReceive, IntentDistribution,
IntentCreateAccount, IntentStatefulSwap/IntentStatelessSwap, IntentFundSwap.
Actions (e.g. ActionPublicTransfer, ActionPublicWithdraw, ActionOpenAccount,
ActionFeePayment) each construct their transaction from server parameters and
contribute Ed25519 signatures.
internal/solana/programs/ (~33 builders) encode the on-chain instructions:
TimelockProgram (vault lifecycle), VirtualMachineProgram (VM swaps),
TokenProgram/AssociatedTokenProgram (SPL), CurrencyCreatorProgram
(InitializeCurrency/BuyTokens/SellTokens/InitializePool),
CoinbaseStableSwapperProgram (USDC↔USDF), ComputeBudgetProgram, SystemProgram,
MemoProgram.
A transactor (internal/transactors/) is a coroutine-scoped state machine for a
multi-step, multi-RPC workflow that coordinates messaging + an intent. Lifecycle:
with(...) configures (token, amount, rendezvous payload), start() runs the flow,
dispose() cancels the scope.
GiveBillTransactor/GrabBillTransactor— the device-to-device cash-bill exchange: advertise on the messaging stream, wait for the counterpart, then submit the transfer/remote-receive intent.SendGiftCardTransactor/ReceiveGiftCardTransactor— gift-card (link) flow.Transactor<E>is the base (provideslogAndFail);PayloadFactorybuilds theOpenCodePayload,AccountClusterFactoryderives clusters.
Buy/sell of a launchpad currency against USDF runs through StatefulSwapExecutor
(escrowed state machine: SwapState CREATED→FUNDED→COMPLETED) or
StatelessSwapExecutor (atomic, signed up-front). SwapRepository/SwapService
expose it; SwapFundingSource selects how the swap is funded.
exchange/Exchange (impl internal/exchange/OpenCodeExchange) streams and caches
rates and tracks the preferred currency. Every payment carries a
cryptographically-signed rate proof: VerifiedProtoManager seals a VerifiedState
into an immutable VerifiedFiat (via VerifiedFiatCalculator), so the amount and the
rate it was computed at can't drift. See
06.
providers/ exposes SessionListener (the login/logout hook coordinators implement)
and TokenMetadataProvider.
- Proto — update
definitions/opencodeand regenerate (13). - Intent — add an
IntentType+ itsActionTypes underinternal/network/api/intents/; add any newinternal/solana/programs/instruction. - Api/Service/Executor — extend
TransactionApiandTransactionService(or add an executor for a new streaming flow). - Repository — add the method to the repository interface +
Internal*Repository(with typed-error mapping / retry where relevant). - Controller — expose it on the public controller.
- 04 — Networking · 06 — Payments & operations · 13 — Protobuf & codegen · 14 — Error handling
- Sibling:
services/flipcash· Compose bindings:services/opencode-compose