Capsule

This SDK empowers Flutter developers to build highly functional, deeply expressive wallets powering seamless user experiences.

Features

  • Capsule class is the main entry point for the SDK. See implemented methods.
  • Tested on iOS and Android.
  • On iOS, you can use a @test.usecapsule.com email to bypass verification for testing.

Prerequisites

You'll need an API key from the Capsule team. Please request one if you don't have one yet.

If you're building for iOS, install Xcode.

Install Flutter. Here are the steps on MacOS (see flutter.dev for more info).

mkdir ~/repos
cd repos
git clone git@github.com/flutter/flutter
echo 'export PATH=$PATH:~/repos/flutter/bin' >> ~/.zshenv
source ~/.zshenv
flutter channel stable
flutter doctor

Getting started

Create a mobile application with flutter create if you don't already have one.

flutter create my_application
cd my_application

From the root of your Flutter application, add the Capsule Flutter SDK:

`flutter pub add capsule`

You can now import the Capsule class in your Flutter code.

import 'package:capsule/capsule.git';

final capsule = Capsule(
  environment: Environment.beta,
  apiKey: '<YOUR API KEY>',
)..init();

Compile and run.

flutter run

Usage

User Creation

await capsule.createUser('test@example.com');
final webAuthURL = await capsule.verifyEmail('123456');

If you are using a simulator, make sure that it is enrolled in Face ID

final biometricsId = await _capsule.verifyEmail(verificationCode);
await _capsule.generatePasskey(email, biometricsId);
final wallet = (await _capsule.createWallet(
    skipDistribute: false,
  )).wallet;

Signing a transaction

final result = await _capsule.signMessage(
  walletId: wallet.id,
  messageBase64: base64Encode(
    hex.decode(
      // hello
      '1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8',
    ),
  ),
);
if (result is SuccessfulSignatureResult) {
  // It worked! See `result.signature`
}

Signing using typed data

final signer = CapsuleSigner(capsule);
final msgParams = {
  'domain': {
    // This defines the network.
    'chainId': '4',
    // Give a user-friendly name to the specific contract you're signing for.
    'name': 'Ether Mail',
    // Add a verifying contract to make sure you're establishing contracts with the proper entity.
    'verifyingContract': '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
    // This identifies the latest version.
    'version': '1',
  },

  // This defines the message you're proposing the user to sign, is dapp-specific, and contains
  // anything you want. There are no required fields. Be as explicit as possible when building out
  // the message schema.
  'message': {
    'contents': 'Hello, Bob!',
    'attachedMoneyInEth': 4.2,
    'from': {
      'name': 'Cow',
      'wallets': [
        '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
        '0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF',
      ],
    },
    'to': [
      {
        'name': 'Bob',
        'wallets': [
          '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
          '0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57',
          '0xB0B0b0b0b0b0B000000000000000000000000000',
        ],
      },
    ],
  },
  // This refers to the keys of the following types object.
  'primaryType': 'Mail',
  'types': {
    // This refers to the domain the contract is hosted on.
    'EIP712Domain': [
      {'name': 'name', 'type': 'string'},
      {'name': 'version', 'type': 'string'},
      {'name': 'chainId', 'type': 'uint256'},
      {'name': 'verifyingContract', 'type': 'address'},
    ],
    // Not an EIP712Domain definition.
    'Group': [
      {'name': 'name', 'type': 'string'},
      {'name': 'members', 'type': 'Person[]'},
    ],
    // Refer to primaryType.
    'Mail': [
      {'name': 'from', 'type': 'Person'},
      {'name': 'to', 'type': 'Person[]'},
      {'name': 'contents', 'type': 'string'},
    ],
    // Not an EIP712Domain definition.
    'Person': [
      {'name': 'name', 'type': 'string'},
      {'name': 'wallets', 'type': 'address[]'},
    ],
  },
};
final result = await signer.signTypedData(
  from: wallet.address!,
  data: msgParams,
  version: SignTypedDataVersion.v4,
);
if (result is SuccessfulSignatureResult) {
  // It worked! See `result.signature`
}

Documentation

Check out the Developer Documentation to get started!

Libraries

capsule