Magma makes it easy to send Tezos with new Contacts feature

By Simon McLoughlin, Malena López-Batista, Michael Pastko

on September 25, 2020

The camlCase team is excited to debut a brand new Contacts feature in the latest release of Magma, the first major upgrade to our wallet since launching this July. The Contacts feature makes it easy to save Tezos wallet addresses as named contacts so it's both faster to send Tez to your friends and other personal wallets, as well as to quickly and easily identify recipients and senders in your transaction history.

Using your phone's built-in contacts functionality

We designed this feature with three principles in mind: privacy, portability, and decentralization. This ruled out building our own server-side application to store and manage contacts, since camlCase neither requires nor desires to store this type of user data; we'd rather build such functionality in a way that we don't have to know about these contacts at all.

Next we considered building our own contacts list stored inside the application bundle, but we quickly realized this would offer a subpar user experience since losing your phone or performing a factory reset would erase these contacts. Further, it would not allow multiple devices held by the same owner to access a shared list of contacts.

This is when we turned our attention to the device's in-built contact book functionality. The device owner has full control over whether or not these contacts are backed up online, which service to use, and whether to share contacts across devices. Using the contact book also means these wallet addresses, contact names, and any other related data never need to be copied to our servers while still providing full functionality to end users.

A final benefit of using the device's built-in contact book is that saved wallet addresses may be optionally accessed by other applications when a user grants permission. This means that contacts created in Magma could be used by other Tezos apps without needing to copy and paste or manually port data.

How does it work?

First, make sure you are using the latest version of Magma, 1.1.x. You will notice a new Contacts tab in the bottom navigation bar, which is where you'll go to manage your contacts. The first time you add a new contact, you will be prompted to give Magma permission to access your device's contact book. Magma will never: access contacts without permission, store your contacts anywhere else, or send messages to your contacts. Your permission is needed for the feature to work, and it can be revoked at any time.

There are three ways to add a new contact:

1) From the 'Add a Contact' screen, type in a name for your contact and either manually enter a Tezos wallet address or use the QR code scanning feature to input the wallet address. Tap the 'Save Contact' button and you're all set.

 

2) From the 'Add a Contact' screen, tap the contact search icon to open your device's contact book and select a contact to fill the 'Name' field for your Magma contact. Then either manually enter a Tezos wallet address or use the QR code scanning feature to input the wallet address. Tap the 'Save Contact' button and the wallet address will be appended to your existing contact. This makes it easy to transfer Tezos to friends and family members already stored to your device.

 

3) From the ‘Send’ screen, after entering a valid Tezos wallet address, an ‘Add to Contacts’ button will appear that will redirect you to the ‘Add a Contact’ screen, where you’ll be able to enter a contact name or select one from your device as Options 1 and 2 describe above, with the wallet address populated from the ‘Send’ screen.

As you add Contacts to Magma, you will see their names appear in place of wallet addresses on screens such as your Wallet Activity feed, making it easier to quickly scan and find names you will recognize. We've even built out a contact-specific transaction history screen so you can view just the transactions between you and an individual contact.

 

You will also find updates to the ‘Send’ feature that incorporate our new Contacts functionality. In addition to scanning a QR code or manually entering a recipient wallet address, you will now be able to select a recipient from a list of your saved Contacts. Simply tap a contact name to continue to select a token type and amount to send.

 

Technical Details: Creating a standard for the community

Now that we’ve walked through how to use the Contacts feature, let’s take a look under the hood. One added benefit to using the device’s built-in contact book is that the device owner has the ability to grant other apps access to Tezos wallet addresses saved to contacts by Magma. Below are some technical details to help other apps access the contacts Magma creates, and how to create their own:

Storing the information

We needed to find a way to store the information so that it would be accessible by Android and iOS devices. Both of these platforms have the ability to add extra data to contacts but come with different restrictions on the type of data that can be entered. Both platforms share an “Instant Message” field that allows freeform data that suits our needs. Since some other wallet applications allow users to switch between mainnet and testnet nodes, we also wanted to make sure that we supported this feature while preventing ambiguity in its implementation.

To this end, Magma stores a contact’s Tezos address as an “Instant Message” field, with the label “Tezos Address” and the value <network>//<address>. Magma currently only allows one address per network per contact.

e.g.

 

These records can be created with code like this:

iOS / Swift:

let contact = CNMutableContact()
contact.givenName =John Appleseed
let addressString = "mainnet//tz1abc123"
let instantMessageAddress = CNInstantMessageAddress(username: addressString, service: "Tezos Address")

let instantMessage = CNLabeledValue<CNInstantMessageAddress>(label: "Tezos Address" value: instantMessageAddress)
    
contact.instantMessageAddresses.append(instantMessage)

Android / Kotlin:

To add the IM Mimetype to an existing entry, you can use the raw_contact_id of the contact given by the Android ContentProvider.

val addressString = "mainnet//tz1abc123"
val operations = ArrayList<ContentProviderOperation>()
	operations.add(
	ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
	            .withValue(ContactsContract.Data.RAW_CONTACT_ID, raw_contact_id)
	            .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE)
	            .withValue(ContactsContract.CommonDataKinds.Im.DATA, addressString)
	            .withValue(ContactsContract.CommonDataKinds.Im.TYPE, ContactsContract.CommonDataKinds.Im.TYPE_OTHER)
	            .withValue(
	                ContactsContract.CommonDataKinds.Im.PROTOCOL,
	                ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM
	            )
	            .withValue(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL, "Tezos Address")
	            .build()
	)
	contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)

Reading the information

These records can be read from a contact using a function like this:

iOS / Swift:

func tezosAddress(_ contact: CNContact) -> String? {
    var tezosData: String? = nil

    for instantMessage in contact.instantMessageAddresses {
        if instantMessage.label ==Tezos Address{

            let username = instantMessage.value.username
            let network = username.components(separatedBy: "//").first

            // Only return data, if it is using the same network
            if let n = network, n == "mainnet" {
                tezosData = instantMessage.value.username
                break
            }
        }
    }

    return tezosData
}

Android / Kotlin:

val projection = arrayOf(Data.DATA1)
val selection = Data.MIMETYPE + " = '" + Im.CONTENT_ITEM_TYPE + "' " +
"AND " + Data.RAW_CONTACT_ID + " = '" + raw_contact_id + "' " +
"AND " + Im.CUSTOM_PROTOCOL + " = 'Tezos Address' " +
"AND " + Im.DATA + " LIKE 'mainnet//%'"     

var tezosAddress = ""
contentResolver.query(
    Data.CONTENT_URI,
    projection,
    selection,
    null,
    null
)?.apply {
    while (this.moveToNext()) {
        val mainnetAddress = this.getString(0)
        val split = formatted.split("//")
        if (split.size == 2) {
            tezosAddress = split[1]
        }
         tezosAddress
    }

    this.close()
}

return tezosAddress

Magma Wallet is open source, so the fill code for interacting with the contacts book can be found here:

camlCase logo
twittercamlCaseTech

©2020 camlCase