Mobile
Protocolo Mobile Wallet Adapter: ECDH, AES-GCM e JSON-RPC

Protocolo Mobile Wallet Adapter: ECDH, AES-GCM e JSON-RPC

Referência de Métodos JSON-RPC

Uma vez estabelecida uma sessão segura, o dApp se comunica com a carteira usando JSON-RPC 2.0 sobre o canal criptografado. Esta lição documenta cada método, seus parâmetros e formatos de resposta.

Categorias de Métodos

O MWA define duas categorias de métodos:

Métodos Não Privilegiados: Podem ser chamados a qualquer momento após o estabelecimento de sessão.

  • authorize

  • deauthorize

  • get_capabilities

Métodos Privilegiados: Requerem chamada authorize prévia com sucesso.

  • sign_and_send_transactions

  • sign_messages

  • clone_authorization

authorize

Solicita autorização do usuário para o dApp.

Solicitação

json
{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "authorize",
  "params": {
    "identity": {
      "uri": "https://myapp.com",
      "icon": "favicon.ico",
      "name": "Meu dApp"
    },
    "cluster": "mainnet-beta",
    "auth_token": "previo...cao",
    "features": ["sign_and_send_transactions:2", "sign_messages"],
    "addresses": ["base64_encoded_addresses"]
  }
}

Parâmetros

ParâmetroTipoObrigatórioDescrição
identityobjectSimInformações sobre o dApp para exibição
identity.uristringSimURI para verificação de identidade
identity.iconstringNãoCaminho relativo do ícone a partir de identity.uri
identity.namestringNãoNome de exibição do dApp
clusterstringNãoCluster Solana: mainnet-beta, testnet, devnet, ou URL customizada
auth_tokenstringNãoAuth token anterior para reautorização silenciosa
featuresstring[]NãoLista de features/versões que o dApp requer
addressesstring[]NãoEndereços de conta específicos para autorizar

Resposta (Sucesso)

json
{
  "jsonrpc": "2.0",
  "id": "1",
  "result": {
    "accounts": [
      {
        "address": "base64_encoded_pubkey",
        "display_address": "7xKXt...",
        "display_address_format": "base58",
        "label": "Carteira Principal",
        "icon": "data:image/png;base64,...",
        "chains": ["solana:mainnet"],
        "features": ["solana:signAndSendTransaction"]
      }
    ],
    "auth_token": "novo_ou...token",
    "wallet_uri_base": "https://phantom.app/ul/v1/",
    "sign_in_result": { /* se sign-in foi solicitado */ }
  }
}

Campos da Resposta

CampoTipoDescrição
accountsarrayContas autorizadas
accounts[].addressstringChave pública codificada em base64
accounts[].display_addressstringEndereço legível
accounts[].display_address_formatstringFormato: base58
accounts[].labelstringNome opcional da conta
accounts[].iconstringÍcone opcional da conta (data URI)
accounts[].chainsstring[]Identificadores de chain (formato CAIP-2)
accounts[].featuresstring[]Features suportadas pela conta
auth_tokenstringToken para reautorização futura
wallet_uri_basestringURI base para deep links específicos da carteira

Fluxo de Reautorização

Se auth_token for fornecido e ainda válido, a carteira pode pular interação do usuário:

text
dApp                                     Carteira
  │                                        │
  │ authorize(auth_token="abc123")         │
  │──────────────────────────────────────►│
  │                                        │ Token válido?
  │                                        │ Sim → sucesso silencioso
  │◄──────────────────────────────────────│
  │ result: { accounts, auth_token }       │

Se o token for inválido ou expirado:

text
  │ authorize(auth_token="expirado")       │
  │──────────────────────────────────────►│
  │                                        │ Token inválido
  │                                        │ Mostrar UI de autorização
  │                                        │ Usuário aprova
  │◄──────────────────────────────────────│
  │ result: { accounts, new_auth_token }   │

deauthorize

Invalida uma autorização prévia.

Solicitação

json
{
  "jsonrpc": "2.0",
  "id": "2",
  "method": "deauthorize",
  "params": {
    "auth_token": "token_...data"
  }
}

Parâmetros

ParâmetroTipoObrigatórioDescrição
auth_tokenstringSimO auth token para invalidar

Resposta (Sucesso)

json
{
  "jsonrpc": "2.0",
  "id": "2",
  "result": {}
}

Casos de Uso

  1. Logout do usuário: Quando o usuário explicitamente desconecta sua carteira

  2. Segurança: Após detectar atividade suspeita

  3. Rotação de token: Antes de solicitar autorização fresca

get_capabilities

Consulta as features suportadas pela carteira.

Solicitação

json
{
  "jsonrpc": "2.0",
  "id": "3",
  "method": "get_capabilities"
}

Sem parâmetros necessários.

Resposta

json
{
  "jsonrpc": "2.0",
  "id": "3",
  "result": {
    "supports_clone_authorization": true,
    "supports_sign_and_send_transactions": true,
    "max_transactions_per_request": 10,
    "max_messages_per_request": 10,
    "supported_transaction_versions": ["legacy", 0]
  }
}

Campos da Resposta

CampoTipoDescrição
supports_clone_authorizationbooleanSe clone_authorization está disponível
supports_sign_and_send_transactionsbooleanSe a carteira pode assinar e transmitir
max_transactions_per_requestnumberMáximo de transações em uma solicitação
max_messages_per_requestnumberMáximo de mensagens em uma solicitação
supported_transaction_versionsarray"legacy", 0 (versionada), etc.

Por Que Verificar Capacidades?

Carteiras diferentes têm limites e features diferentes. Verifique capacidades antes de:

  • Enviar lotes grandes de transações

  • Usar transações versionadas

  • Assumir suporte a clone_authorization

sign_and_send_transactions

Assina transações e as transmite para a rede.

Solicitação

json
{
  "jsonrpc": "2.0",
  "id": "4",
  "method": "sign_and_send_transactions",
  "params": {
    "payloads": [
      "base64_encoded_transaction_1",
      "base64_encoded_transaction_2"
    ],
    "options": {
      "min_context_slot": 150000000,
      "commitment": "confirmed",
      "skip_preflight": false,
      "max_retries": 3
    }
  }
}

Parâmetros

ParâmetroTipoObrigatórioDescrição
payloadsstring[]SimTransações serializadas codificadas em base64
optionsobjectNãoOpções de envio da transação
options.min_context_slotnumberNãoSlot mínimo para contexto
options.commitmentstringNãoprocessed, confirmed, finalized
options.skip_preflightbooleanNãoPular simulação preflight
options.max_retriesnumberNãoNúmero de tentativas de envio

Formato do Payload de Transação

Cada payload é uma transação Solana, serializada de acordo com sua versão:

  • Transações legacy: Serialização padrão

  • Transações versionadas (v0): Incluem prefixo de versão e tabelas de lookup de endereço

A transação deve:

  1. Ter recentBlockhash definido (ou null para a carteira preencher)

  2. Deixar a assinatura do fee payer vazia (a carteira preenche)

  3. Incluir todas as outras assinaturas necessárias

Resposta (Sucesso)

json
{
  "jsonrpc": "2.0",
  "id": "4",
  "result": {
    "signatures": [
      "base64_encoded_signature_1",
      "base64_encoded_signature_2"
    ]
  }
}

Resposta (Falha Parcial)

json
{
  "jsonrpc": "2.0",
  "id": "4",
  "error": {
    "code": -3,
    "message": "Algumas transações não foram assinadas",
    "data": {
      "valid": [0],
      "invalid": [1]
    }
  }
}

Códigos de erro para este método:

  • -1: Autorização falhou

  • -2: Payloads inválidos (transações malformadas)

  • -3: Nem todas transações assinadas (usuário recusou algumas)

  • -4: Não enviado (transmissão falhou)

  • -6: Payloads demais (excede max_transactions_per_request)

sign_messages

Assina mensagens arbitrárias (não transações).

Solicitação

json
{
  "jsonrpc": "2.0",
  "id": "5",
  "method": "sign_messages",
  "params": {
    "payloads": [
      "base64_encoded_message"
    ],
    "addresses": [
      "base64_encoded_pubkey"
    ]
  }
}

Parâmetros

ParâmetroTipoObrigatórioDescrição
payloadsstring[]SimMensagens codificadas em base64 para assinar
addressesstring[]NãoContas específicas para assinar (devem estar autorizadas)

Resposta (Sucesso)

json
{
  "jsonrpc": "2.0",
  "id": "5",
  "result": {
    "signed_payloads": [
      "base64_encoded_signed_message"
    ]
  }
}

Formato de Assinatura de Mensagem

Mensagens são assinadas como estão (bytes brutos). Para mensagens legíveis, codifique como UTF-8 primeiro.

Padrões comuns:

  • Autenticação: Assinar um nonce para provar propriedade da carteira

  • Dados off-chain: Assinar dados estruturados para protocolos como Sign-In With Solana

clone_authorization

Cria uma nova autorização a partir de uma existente.

Solicitação

json
{
  "jsonrpc": "2.0",
  "id": "6",
  "method": "clone_authorization",
  "params": {}
}

Sem parâmetros (usa a autorização da sessão atual).

Resposta (Sucesso)

json
{
  "jsonrpc": "2.0",
  "id": "6",
  "result": {
    "auth_token": "***"
  }
}

Casos de Uso

  1. Apps multi-sessão: Clonar autorização para usar em serviços em segundo plano

  2. Refresh de token: Obter um novo token enquanto a sessão atual ainda é válida

Este método pode não ser suportado por todas as carteiras (verifique get_capabilities).

Formato de Resposta de Erro

Todos os métodos retornam erros no formato JSON-RPC:

json
{
  "jsonrpc": "2.0",
  "id": "1",
  "error": {
    "code": -1,
    "message": "Mensagem de erro legível",
    "data": { /* dados adicionais opcionais */ }
  }
}

Códigos de Erro Padrão

CódigoNomeDescrição
-32700Erro de parseJSON inválido
-32600Solicitação inválidaJSON-RPC não válido
-32601Método não encontradoNome de método desconhecido
-32602Parâmetros inválidosParâmetros de método inválidos
-32603Erro internoErro interno da carteira

Códigos de Erro Específicos do MWA

CódigoNomeDescrição
-1ERROR_AUTHORIZATION_FAILEDUsuário recusou ou autorização inválida
-2ERROR_INVALID_PAYLOADSPayloads de transação/mensagem malformados
-3ERROR_NOT_SIGNEDUsuário recusou assinar (alguns ou todos)
-4ERROR_NOT_SUBMITTEDFalha no envio da transação
-5ERROR_NOT_CLONEDFalha na clonagem de autorização
-6ERROR_TOO_MANY_PAYLOADSExcede limite da carteira
-7ERROR_CHAIN_NOT_SUPPORTEDChain solicitada não suportada pela carteira
-100ERROR_ATTEST_ORIGIN_ANDROIDAttestation Android necessário mas falhou

Strings de Feature

Strings de feature usam um formato namespacificado para identificar capacidades e versões:

FeatureDescrição
solana:signTransactionsAssinar transações sem enviar
solana:signAndSendTransactionAssinar e transmitir uma única transação
solana:signAndSendTransaction:2Versão 2 com suporte a opções adicionais
solana:signMessagesSuporte a assinatura de mensagens
solana:cloneAuthorizationSuporte a clonagem de autorização

Ao chamar authorize, especifique as features necessárias:

json
{
  "method": "authorize",
  "params": {
    "features": ["solana:signAndSendTransaction:2", "solana:signMessages"]
  }
}

A carteira retornará erro se não puder suportar as features solicitadas.

Identificadores de Chain

Respostas de contas incluem identificadores de chain no formato CAIP-2:

ChainIdentificador CAIP-2
Mainnet Betasolana:mainnet
Devnetsolana:devnet
Testnetsolana:testnet
Localnetsolana:localnet

Fluxo de Solicitação/Resposta

Uma sessão completa com múltiplas operações:

text
dApp                                        Carteira
  │                                           │
  │ ──── HELLO_REQ ─────────────────────────► │
  │ ◄─── HELLO_RSP ───────────────────────── │
  │                                           │
  │ ──── authorize ──────────────────────────► │
  │                                           │ [Usuário aprova]
  │ ◄─── result: accounts, auth_token ─────── │
  │                                           │
  │ ──── sign_and_send_transactions ─────────► │
  │                                           │ [Usuário revisa]
  │ ◄─── result: signatures ───────────────── │
  │                                           │
  │ ──── deauthorize ────────────────────────► │
  │ ◄─── result: {} ──────────────────────── │
  │                                           │
  │ ──── [WebSocket close] ──────────────────► │

Melhores Práticas

1. Sempre Verifique Autorização

Antes de métodos privilegiados:

typescript
if (!authToken) {
  const authResult = await wallet.authorize(identity);
  authToken = authResult.auth_token;
}

2. Lidar com Assinatura Parcial

Verifique o código de erro -3 que indica assinatura parcial:

typescript
try {
  const result = await wallet.signAndSendTransactions(payloads);
} catch (error) {
  if (error.code === -3 && error.data) {
    console.log('Assinadas:', error.data.valid);
    console.log('Recusadas:', error.data.invalid);
  }
}

3. Respeitar Limites da Carteira

Consulte capacidades e agrupe adequadamente:

typescript
const caps = await wallet.getCapabilities();
const batchSize = caps.max_transactions_per_request || 1;

for (let i = 0; i < transactions.length; i += batchSize) {
  const batch = transactions.slice(i, i + batchSize);
  await wallet.signAndSendTransactions(batch);
}

4. Usar Reautorização

Armazene e reutilize auth_token:

typescript
// Ao iniciar o app
const savedToken = await AsyncStorage.getItem('auth_token');

// Em transact()
const result = await wallet.authorize({
  identity,
  auth_token: savedToken
});

// Salvar para próxima vez
await AsyncStorage.setItem('auth_token', result.auth_token);

5. Validar Respostas

Verifique a estrutura da resposta antes de acessar campos:

typescript
if (result.accounts && result.accounts.length > 0) {
  const address = result.accounts[0].address;
  // Usar address...
}

Na próxima lição, examinamos verificação de identidade: como carteiras confirmam que o dApp é quem diz ser.

Blueshift © 2026Commit: 1b88646