Skip to main content

Wallet Access

userWallets

Get the current list of user wallets (synchronous).
List<BaseWallet> get userWallets

Example

final wallets = sdk.wallets.userWallets;
for (final wallet in wallets) {
  print('${wallet.address} (${wallet.chain})');
}

userWalletsChanges

Observe wallet list changes (reactive stream).
Stream<List<BaseWallet>> get userWalletsChanges

Example

sdk.wallets.userWalletsChanges.listen((wallets) {
  // Handle wallet changes
  setState(() {
    this.wallets = wallets;
  });
});

StreamBuilder Example

StreamBuilder<List<BaseWallet>>(
  stream: sdk.wallets.userWalletsChanges,
  initialData: sdk.wallets.userWallets,
  builder: (context, snapshot) {
    final wallets = snapshot.data ?? [];

    if (wallets.isEmpty) {
      return Text('No wallets found');
    }

    return ListView.builder(
      itemCount: wallets.length,
      itemBuilder: (context, index) {
        final wallet = wallets[index];
        return ListTile(
          title: Text(wallet.address),
          subtitle: Text(wallet.chain),
          trailing: Text(wallet.walletName ?? 'Unknown'),
        );
      },
    );
  },
)

Balance Operations

getBalance

Get the balance for a specific wallet.
Future<String> getBalance(BaseWallet wallet)

Parameters

  • wallet (BaseWallet) - The wallet to check balance for

Returns

  • String - Balance as a string (in native token units)

Example

try {
  final balance = await sdk.wallets.getBalance(wallet);
  print('Balance: $balance');
} catch (e) {
  print('Failed to get balance: $e');
}

FutureBuilder Example

FutureBuilder<String>(
  future: sdk.wallets.getBalance(wallet),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return CircularProgressIndicator();
    }

    if (snapshot.hasError) {
      return Text('Error: ${snapshot.error}');
    }

    return Text('Balance: ${snapshot.data}');
  },
)

Network Operations

getNetwork

Get the current network for a wallet.
Future<GenericNetwork> getNetwork(BaseWallet wallet)

Parameters

  • wallet (BaseWallet) - The wallet to get network for

Returns

  • GenericNetwork - The current network

Example

try {
  final network = await sdk.wallets.getNetwork(wallet);
  print('Network: ${network.name}, Chain ID: ${network.chainId}');
} catch (e) {
  print('Failed to get network: $e');
}

switchNetwork

Switch a wallet to a different network.
Future<void> switchNetwork(BaseWallet wallet, GenericNetwork network)

Parameters

  • wallet (BaseWallet) - The wallet to switch networks for
  • network (GenericNetwork) - The target network

Example

try {
  final polygon = sdk.networks.evm.firstWhere((n) => n.chainId == 137);
  await sdk.wallets.switchNetwork(wallet, polygon);
  print('Switched to ${polygon.name}');
} catch (e) {
  print('Failed to switch network: $e');
}

Complete Example with Network Selector

class NetworkSwitcher extends StatefulWidget {
  final BaseWallet wallet;

  NetworkSwitcher({required this.wallet});

  @override
  _NetworkSwitcherState createState() => _NetworkSwitcherState();
}

class _NetworkSwitcherState extends State<NetworkSwitcher> {
  final sdk = DynamicSDK.instance;
  GenericNetwork? currentNetwork;
  bool isLoading = false;

  @override
  void initState() {
    super.initState();
    loadCurrentNetwork();
  }

  Future<void> loadCurrentNetwork() async {
    try {
      final network = await sdk.wallets.getNetwork(widget.wallet);
      setState(() {
        currentNetwork = network;
      });
    } catch (e) {
      print('Failed to load network: $e');
    }
  }

  Future<void> switchNetwork(GenericNetwork network) async {
    setState(() => isLoading = true);

    try {
      await sdk.wallets.switchNetwork(widget.wallet, network);
      setState(() {
        currentNetwork = network;
        isLoading = false;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Switched to ${network.name}')),
      );
    } catch (e) {
      setState(() => isLoading = false);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Failed to switch: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    final networks = widget.wallet.chain == 'EVM'
        ? sdk.networks.evm
        : sdk.networks.solana;

    return Column(
      children: [
        if (currentNetwork != null)
          Text('Current: ${currentNetwork!.name}'),
        SizedBox(height: 16),
        if (isLoading)
          CircularProgressIndicator()
        else
          DropdownButton<GenericNetwork>(
            value: currentNetwork,
            items: networks.map((network) {
              return DropdownMenuItem(
                value: network,
                child: Text(network.name),
              );
            }).toList(),
            onChanged: (network) {
              if (network != null) {
                switchNetwork(network);
              }
            },
          ),
      ],
    );
  }
}

Message Signing

signMessage

Sign a message with a wallet.
Future<String> signMessage(BaseWallet wallet, String message)

Parameters

  • wallet (BaseWallet) - The wallet to sign with
  • message (String) - The message to sign

Returns

  • String - The signature

Example

try {
  final signature = await sdk.wallets.signMessage(wallet, 'Hello!');
  print('Signature: $signature');
} catch (e) {
  print('Failed to sign: $e');
}

Complete Example

class MessageSigner extends StatefulWidget {
  final BaseWallet wallet;

  MessageSigner({required this.wallet});

  @override
  _MessageSignerState createState() => _MessageSignerState();
}

class _MessageSignerState extends State<MessageSigner> {
  final sdk = DynamicSDK.instance;
  final messageController = TextEditingController();
  String? signature;
  bool isLoading = false;

  Future<void> signMessage() async {
    setState(() => isLoading = true);

    try {
      final sig = await sdk.wallets.signMessage(
        widget.wallet,
        messageController.text,
      );
      setState(() {
        signature = sig;
        isLoading = false;
      });
    } catch (e) {
      setState(() => isLoading = false);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Failed to sign: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextField(
          controller: messageController,
          decoration: InputDecoration(
            labelText: 'Message to sign',
            hintText: 'Enter your message',
          ),
        ),
        SizedBox(height: 16),
        ElevatedButton(
          onPressed: isLoading ? null : signMessage,
          child: isLoading
              ? CircularProgressIndicator()
              : Text('Sign Message'),
        ),
        if (signature != null) ...[
          SizedBox(height: 16),
          Text('Signature:'),
          SelectableText(
            signature!,
            style: TextStyle(fontSize: 12, fontFamily: 'monospace'),
          ),
        ],
      ],
    );
  }

  @override
  void dispose() {
    messageController.dispose();
    super.dispose();
  }
}

Typed Data Signing (EIP-712)

signTypedData

Sign typed data (EIP-712) with a wallet.
Future<String> signTypedData(BaseWallet wallet, String typedDataJson)

Parameters

  • wallet (BaseWallet) - The wallet to sign with
  • typedDataJson (String) - The typed data as a JSON string

Returns

  • String - The signature

Example

const typedData = '''
{
  "types": {
    "EIP712Domain": [
      {"name": "name", "type": "string"},
      {"name": "chainId", "type": "uint256"}
    ],
    "Person": [
      {"name": "name", "type": "string"}
    ]
  },
  "primaryType": "Person",
  "domain": {
    "name": "My App",
    "chainId": 1
  },
  "message": {
    "name": "Alice"
  }
}
''';

try {
  final signature = await sdk.wallets.signTypedData(wallet, typedData);
  print('Signature: $signature');
} catch (e) {
  print('Failed to sign: $e');
}

Complete Example

class TypedDataSigner extends StatefulWidget {
  final BaseWallet wallet;

  TypedDataSigner({required this.wallet});

  @override
  _TypedDataSignerState createState() => _TypedDataSignerState();
}

class _TypedDataSignerState extends State<TypedDataSigner> {
  final sdk = DynamicSDK.instance;
  String? signature;
  bool isLoading = false;

  Future<void> signTypedData() async {
    setState(() => isLoading = true);

    const typedData = '''
    {
      "types": {
        "EIP712Domain": [
          {"name": "name", "type": "string"},
          {"name": "version", "type": "string"},
          {"name": "chainId", "type": "uint256"}
        ],
        "Person": [
          {"name": "name", "type": "string"},
          {"name": "wallet", "type": "address"}
        ]
      },
      "primaryType": "Person",
      "domain": {
        "name": "My DApp",
        "version": "1",
        "chainId": 1
      },
      "message": {
        "name": "Alice",
        "wallet": "${widget.wallet.address}"
      }
    }
    ''';

    try {
      final sig = await sdk.wallets.signTypedData(widget.wallet, typedData);
      setState(() {
        signature = sig;
        isLoading = false;
      });
    } catch (e) {
      setState(() => isLoading = false);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Failed to sign: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Sign Typed Data (EIP-712)'),
        SizedBox(height: 16),
        ElevatedButton(
          onPressed: isLoading ? null : signTypedData,
          child: isLoading
              ? CircularProgressIndicator()
              : Text('Sign Typed Data'),
        ),
        if (signature != null) ...[
          SizedBox(height: 16),
          Text('Signature:'),
          SelectableText(
            signature!,
            style: TextStyle(fontSize: 12, fontFamily: 'monospace'),
          ),
        ],
      ],
    );
  }
}

Signature Verification

verifySignature

Verify a signature against a message and wallet.
Future<bool> verifySignature({
  required BaseWallet wallet,
  required String message,
  required String signature,
})

Parameters

  • wallet (BaseWallet) - The wallet that signed the message
  • message (String) - The original message
  • signature (String) - The signature to verify

Returns

  • bool - true if signature is valid, false otherwise

Example

try {
  final message = 'Hello!';
  final signature = await sdk.wallets.signMessage(wallet, message);
  final isValid = await sdk.wallets.verifySignature(
    wallet: wallet,
    message: message,
    signature: signature,
  );
  print('Signature is ${isValid ? "valid" : "invalid"}');
} catch (e) {
  print('Verification failed: $e');
}

Complete Example

class SignatureVerifier extends StatefulWidget {
  final BaseWallet wallet;

  SignatureVerifier({required this.wallet});

  @override
  _SignatureVerifierState createState() => _SignatureVerifierState();
}

class _SignatureVerifierState extends State<SignatureVerifier> {
  final sdk = DynamicSDK.instance;
  final messageController = TextEditingController();
  final signatureController = TextEditingController();
  bool? isValid;
  bool isLoading = false;

  Future<void> verifySignature() async {
    setState(() => isLoading = true);

    try {
      final valid = await sdk.wallets.verifySignature(
        wallet: widget.wallet,
        message: messageController.text,
        signature: signatureController.text,
      );
      setState(() {
        isValid = valid;
        isLoading = false;
      });
    } catch (e) {
      setState(() => isLoading = false);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Verification failed: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextField(
          controller: messageController,
          decoration: InputDecoration(
            labelText: 'Original message',
            hintText: 'Enter the message',
          ),
        ),
        SizedBox(height: 12),
        TextField(
          controller: signatureController,
          decoration: InputDecoration(
            labelText: 'Signature',
            hintText: 'Enter the signature',
          ),
        ),
        SizedBox(height: 16),
        ElevatedButton(
          onPressed: isLoading ? null : verifySignature,
          child: isLoading
              ? CircularProgressIndicator()
              : Text('Verify Signature'),
        ),
        if (isValid != null) ...[
          SizedBox(height: 16),
          Text(
            isValid! ? 'Signature is VALID' : 'Signature is INVALID',
            style: TextStyle(
              fontSize: 18,
              fontWeight: FontWeight.bold,
              color: isValid! ? Colors.green : Colors.red,
            ),
          ),
        ],
      ],
    );
  }

  @override
  void dispose() {
    messageController.dispose();
    signatureController.dispose();
    super.dispose();
  }
}

Primary Wallet Management

setPrimary

Set a wallet as the primary wallet for the user.
Future<void> setPrimary(String walletId)

Parameters

  • walletId (String) - The ID of the wallet to set as primary

Example

try {
  final walletId = wallet.id;
  if (walletId != null) {
    await sdk.wallets.setPrimary(walletId);
    print('Set wallet as primary');
  } else {
    print('Wallet ID is null');
  }
} catch (e) {
  print('Failed to set primary: $e');
}

Complete Example with Wallet List

class WalletListWithPrimary extends StatefulWidget {
  @override
  _WalletListWithPrimaryState createState() => _WalletListWithPrimaryState();
}

class _WalletListWithPrimaryState extends State<WalletListWithPrimary> {
  final sdk = DynamicSDK.instance;

  Future<void> setPrimaryWallet(BaseWallet wallet) async {
    final walletId = wallet.id;
    if (walletId == null) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Wallet ID is null')),
      );
      return;
    }

    try {
      await sdk.wallets.setPrimary(walletId);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Set ${wallet.address} as primary')),
      );
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Failed to set primary: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<List<BaseWallet>>(
      stream: sdk.wallets.userWalletsChanges,
      initialData: sdk.wallets.userWallets,
      builder: (context, snapshot) {
        final wallets = snapshot.data ?? [];

        if (wallets.isEmpty) {
          return Center(child: Text('No wallets found'));
        }

        return ListView.builder(
          itemCount: wallets.length,
          itemBuilder: (context, index) {
            final wallet = wallets[index];
            return Card(
              child: ListTile(
                title: Text(wallet.address),
                subtitle: Text(
                  '${wallet.chain} - ${wallet.walletName ?? "Unknown"}',
                ),
                trailing: IconButton(
                  icon: Icon(Icons.star_border),
                  onPressed: () => setPrimaryWallet(wallet),
                  tooltip: 'Set as primary',
                ),
              ),
            );
          },
        );
      },
    );
  }
}

Complete Wallet Management Screen

class WalletManagementScreen extends StatelessWidget {
  final sdk = DynamicSDK.instance;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Wallet Management'),
      ),
      body: StreamBuilder<List<BaseWallet>>(
        stream: sdk.wallets.userWalletsChanges,
        initialData: sdk.wallets.userWallets,
        builder: (context, snapshot) {
          final wallets = snapshot.data ?? [];

          if (wallets.isEmpty) {
            return Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Icon(Icons.wallet, size: 64, color: Colors.grey),
                  SizedBox(height: 16),
                  Text('No wallets found'),
                ],
              ),
            );
          }

          return ListView.builder(
            itemCount: wallets.length,
            itemBuilder: (context, index) {
              final wallet = wallets[index];
              return WalletCard(wallet: wallet);
            },
          );
        },
      ),
    );
  }
}

class WalletCard extends StatelessWidget {
  final BaseWallet wallet;
  final sdk = DynamicSDK.instance;

  WalletCard({required this.wallet});

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: EdgeInsets.all(8),
      child: ExpansionTile(
        title: Text(
          wallet.address.length > 20
              ? '${wallet.address.substring(0, 10)}...${wallet.address.substring(wallet.address.length - 8)}'
              : wallet.address,
        ),
        subtitle: Text('${wallet.chain} - ${wallet.walletName ?? "Unknown"}'),
        children: [
          Padding(
            padding: EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                FutureBuilder<String>(
                  future: sdk.wallets.getBalance(wallet),
                  builder: (context, snapshot) {
                    if (snapshot.connectionState == ConnectionState.waiting) {
                      return Text('Balance: Loading...');
                    }
                    if (snapshot.hasError) {
                      return Text('Balance: Error');
                    }
                    return Text('Balance: ${snapshot.data}');
                  },
                ),
                SizedBox(height: 8),
                FutureBuilder<GenericNetwork>(
                  future: sdk.wallets.getNetwork(wallet),
                  builder: (context, snapshot) {
                    if (snapshot.connectionState == ConnectionState.waiting) {
                      return Text('Network: Loading...');
                    }
                    if (snapshot.hasError) {
                      return Text('Network: Error');
                    }
                    return Text('Network: ${snapshot.data?.name}');
                  },
                ),
                SizedBox(height: 16),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    ElevatedButton(
                      onPressed: () async {
                        final walletId = wallet.id;
                        if (walletId != null) {
                          try {
                            await sdk.wallets.setPrimary(walletId);
                            ScaffoldMessenger.of(context).showSnackBar(
                              SnackBar(content: Text('Set as primary')),
                            );
                          } catch (e) {
                            ScaffoldMessenger.of(context).showSnackBar(
                              SnackBar(content: Text('Failed: $e')),
                            );
                          }
                        }
                      },
                      child: Text('Set Primary'),
                    ),
                    ElevatedButton(
                      onPressed: () {
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder: (context) => MessageSigner(wallet: wallet),
                          ),
                        );
                      },
                      child: Text('Sign Message'),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}