| name | add-mutation |
| description | Use when adding, wrapping, or calling an @ecency/sdk mutation hook in the mobile app (Hive broadcasts like transfer, follow, vote, delegate, community, engine token, or points) |
| argument-hint | ["operation-name"] |
Add Mutation
Wrap an @ecency/sdk mutation hook in src/providers/sdk/mutations/. CLAUDE.md
("SDK Migration") covers the adapter; this file is the procedure plus the Hive authority
rules a wrapper must respect.
1. Create the wrapper
src/providers/sdk/mutations/use<Operation>Mutation.ts. Most wrappers there are exactly
this shape, so copy it verbatim:
import { useTransfer } from '@ecency/sdk';
import { useMutationAuth } from './common';
export function useTransferMutation() {
const { username, authContext } = useMutationAuth();
return useTransfer(username, authContext, 'async');
}
'async' is the broadcast mode, passed as the last positional arg after authContext.
Its index varies by hook, so read the signature rather than assuming a position. Pass it
unless the hook has no such parameter. useBroadcastMutation takes it as
{ broadcastMode: 'async' } inside the options object instead.
useMutationAuth() from ./common.ts (most wrappers import it) returns
{ username, authContext }: currentAccount?.name off selectCurrentAccount, plus
useAuthContext() (src/providers/sdk/useAuthContext.ts) building
{ adapter: createMobilePlatformAdapter({...}), enableFallback: true }. There is no
mobilePlatformAdapter object to import, use the factory.
- Most wrappers take no arguments.
useSetCommunityRoleMutation(community) plus
useUpdateCommunityMutation(community) take the community because the SDK bakes it into
the mutation key. useAccountRelationsUpdateMutation(target, onSuccess, onError) takes
the target plus both callbacks because the SDK bakes them into the mutation options.
- A few files are not broadcasts, so they skip
useMutationAuth, despite
CLAUDE.md still saying all mutation wrappers use it.
useGenerateImageMutation plus the digest hooks in
useNewsletterDigestMutations.ts (useSubscribeDigestMutation,
useLeaveDigestMutation, useUnsubscribeAllDigestsMutation) bind the HiveSigner code
from useAuth() (src/hooks/useAuth.ts): useGenerateImage(username, code).
useClaimPointsMutation instead derives the access token itself, decrypting
currentAccount.local.accessToken with getDigitPinCode(pin) plus decryptKey.
2. Export from the barrel
One line in src/providers/sdk/mutations/index.ts, under the matching domain comment:
export { useTransferMutation } from './useTransferMutation';
3. Call it (from the barrel)
import { useFollowMutation } from '../providers/sdk/mutations';
const followMutation = useFollowMutation();
await followMutation.mutateAsync({ following: data.following });
No SDK hook for the operation?
There is no packages/sdk here. @ecency/sdk is an npm dependency, so there
is no local build step. Use the generic useBroadcastMutation, as
useIgnoreUserMutation.ts does. The positional args:
return useBroadcastMutation(
['hive', 'ignore-user'],
username,
({ following }: { following: string }) => [
buildIgnoreOp(username!, following),
],
undefined,
authContext,
'posting',
{ broadcastMode: 'async' },
);
Pass authority as a plain lowercase string. The parameter is typed AuthorityLevel from
@ecency/sdk ('posting' | 'active' | 'owner' | 'memo'); mobile wrappers use
'posting' or 'active'. Do not import the same-named type from
src/screens/dappBrowser/bridges/bridgeTypes.ts, an unrelated dapp browser union.
'posting': vote, comment, reblog, follow, ignore, community roles
'active': transfer, delegate, power up/down, savings, limit orders, proposal vote,
witness proxy
The SDK's exported OPERATION_AUTHORITY_MAP is the reference list. It maps both
account_update plus account_update2 to 'active' flatly, which is right for the common
cases but wrong at both ends, so do not copy it for either one.
Both account update operations have payload-dependent authority (custom_json is the third
payload-dependent case, below):
| Operation | Payload sets | Authority Hive requires |
|---|
| both | owner | 'owner' |
account_update2 | only posting_json_metadata (profile edit, pinned post) | 'posting' |
| both | anything else | 'active' |
The owner row covers BOTH versions.
src/utils/hiveOperationAuthority.ts implements the posting row plus the active row for
account_update2 only. It does NOT implement the owner row for either version: the function is
typed (operation: Operation) => 'posting' | 'active', so its owner branch returns
'active', account_update has no branch at all, plus hiveOperationAuthority.test.ts has
no owner case. Its doc comment calling v1 "correctly resolves to active" is wrong whenever the
payload sets owner. That resolver
serves the hive-uri path (src/providers/hive/hive.ts and
src/hooks/useLinkProcessor.tsx), where the operations arrive from an external link.
Treat an owner change as unsupported and reject it rather than routing
it to 'active'.
custom_json is the third payload-dependent case. required_auths alone means active,
required_posting_auths alone means posting.
A payload populating BOTH is the case this app does not handle. resolveTxRequiredAuthority
collapses a whole transaction to a single 'posting' | 'active', which
src/providers/hive/hive.ts then turns into one decrypted key, posting or active. So treat a
mixed payload as unsupported by this client and say so, rather than calling it malformed or
picking one authority for it.
Neither implementation detects the case today. hiveOperationAuthority.ts never reads
required_posting_auths at all. The SDK's getCustomJsonAuthority returns 'active' as soon
as required_auths is non-empty, without checking the other list. Both therefore sign with one
key.
useBroadcastMutation takes authority as its
sixth positional parameter, fixed when the hook is created, while operations is
(payload: T) => Operation[] and only runs at mutate time. The mutation reads the closed-over
value, so a wrapper cannot choose an authority from its payload. If both shapes are possible,
write two hooks with fixed authorities and pick at the call site:
export const useUpdateProfileMetadataMutation = () => { };
export const useUpdateAccountKeysMutation = () => { };
useBroadcastMutation's authority parameter defaults to 'posting', so pass the right
value explicitly.
Prefer an SDK build<Operation>Op helper (buildTransferOp, buildVoteOp) over a hand
written op tuple.
Gotchas
- Auth is not your job: the adapter routes PIN key decryption, HiveSigner
hive-uri
WebView signing, HiveAuth signing, plus the active key upgrade sheet.
- No toasts or navigation in the wrapper. Do that at the call site or in hook callbacks.
- Check the hook exists in the installed
@ecency/sdk first, then run yarn lint plus
yarn typecheck; the baseline is empty, so any error fails CI.