Skip to content

Latest commit

 

History

History
953 lines (764 loc) · 14.5 KB

dapps-communication.md

File metadata and controls

953 lines (764 loc) · 14.5 KB

Wallet <> DApp communication

Initial meta issue: #13

Authors: G.Libert, N.Seva

Status: Draft

Version: 0.4

Abstract

This standard defines a protocol for communication between a cryptocurrency wallet and a decentralized application (DApp) running in a web browser. The protocol provides a standard interface for DApps to request user authorization for blockchain transactions, as well as a standard mechanism for wallets to sign and broadcast those transactions. By following this standard, DApp developers can provide a seamless user experience for users of any wallet that implements the protocol, while wallet developers can ensure that their products are compatible with a wide range of DApps.

Targeted Audience

This standard is targeted towards developers who are building decentralized applications (DApps) and cryptocurrency wallets that need to interact with each other in a browser environment.

The standard assumes a working knowledge of web development and blockchain technology. It is also assumed that the reader has experience with browser extensions and understands the basic principles of secure communication between web pages and extensions.

NOTE: For more information on content scripts and background scripts, see MDN's browser extensions pages.

Specification

Event-Based Communication

To facilitate communication between a DApp and a wallet, an event-based messaging system will be employed. There are two types of events that can be used in this system:

  1. Events used by the wallet extension to communicate with the web page. These events are triggered on a static target, specifically an invisible HTML element that is attached to the document body and assigned an ID of massaWalletProvider.

  2. Events used by the web page to communicate with the wallet extension. These events are triggered on an extension-specific target, which is an invisible HTML element with an ID of massaWalletProvider-<wallet provider name>, also attached to the document body.

For instance, if the wallet provider name is "AwesomeWallet," the second target ID for this extension would be the element returned by document.getElementById('massaWalletProvider-AwesomeWallet').

It's worth noting that this implementation follows the considerations set forth by Mozilla in this section.

Commands

Register

This event is used by the extension to register itself to the webpage as a wallet provider.

Direction Type Format Example
Extension to webpage register
type: 
  object
properties:  
  address:
    type: string
required:
  - address
{
  "id": "awesomeWalletprovider",
  "name": "Your Awesome Wallet Provider"
}

Account

List

This event is used by the webpage to list known accounts by the extension.

Direction Type Format Example
Webpage to extension account.list none null
Extension to webpage account.list.response
type: array
items:
  type: object
  properties:
    address:
      type: string
      format: base58check
    name:
      type: string
  required:
    - address
[
  {
    "address": "A12...",
    "name": "Account 1"
  },
  {
    "address": "A12..."
  },
]
Balance
Direction Type Format Example
Webpage to extension account.balance
type: object
properties:
  address:
    type: string
    format: base58check
required:
  - address
{
  "address": "A12..."
}
Extension to webpage account.balance.response
type: object
properties:
  finalBalance:
    type: string
    format: BigInt
  candidateBalance:
    type: string
    format: BigInt
required:
  - finalBalance
  - candidateBalance
{
  "finalBalance": "1000",
  "currentBalance": "10"
}
Delete
Direction Type Format Example
Webpage to extension account.delete
type: object
properties:
  address:
    type: string
    format: base58check
required:
  - address
{
  "address": "A12..."
}
Extension to webpage account.delete.response
type: object
properties:
  response:
    type: string
    enum: ["OK", "REFUSED", "ERROR"]
  message:
    type: string
required:
  - response
{
  "response": "REFUSED"
}
Import
Direction Type Format Example
Webpage to extension account.import
type: object
properties:
  privateKey:
    type: string
    format: base58check
  publicKey:
    type: string
    format: base58check
required:
  - privateKey
  - publicKey
{
  "privateKey": "S12...",
  "publicKey": "P12...",
}
Extension to webpage account.import.response
type: object
properties:
  response:
    type: string
    enum: ["OK", "REFUSED", "ERROR"]
  message:
    type: string
required:
  - response
{
  "response": "ERROR",
  "message": "No connection with blockchain"
}

Sign

Direction Type Format Example
Webpage to extension account.sign
type: object
properties:
  address:
    type: string
    format: base58check
  data:
    type: array
    items:
      type: integer
      format: uint8
required:
  - address
  - data
{
 "address": "A12...",
 "data": [49, 3, 255]
}
Extension to webpage account.sign.response
type: object
properties:
  signature:
    type: array
    items:
      type: integer
      format: uint8
  publicKey:
    type: string
    format: base58check
required:
  - signature
  - publicKey
{
 "signature": [49, 3, 255],
 "publicKey": "P12..."
}

Rolls

Sell
Direction Type Format Example
Webpage to extension account.sellRolls
type: object
properties:
  fee:
    type: string
    format: BigInt
  amount:
    type: string
    format: BigInt
required:
  - fee
  - amount
{
 "fee": "12000000",
 "amount": "10"
}
Extension to webpage account.sellRolls.response
type: object
properties:
  operationId:
    type: string
required:
  - operationId
{
 "operationId": "O1sBc7PanPjB8tEadNC4t4GGPFM5kqC8yTKqwzHHV9q7FksuBoE"
}
Buy
Direction Type Format Example
Webpage to extension account.buyRolls
type: object
properties:
  fee:
    type: string
    format: BigInt
  amount:
    type: string
    format: BigInt
required:
  - fee
  - amount
{
 "fee": "12000000",
 "amount": "10"
}
Extension to webpage account.buyRolls.response
type: object
properties:
  operationId:
    type: string
required:
  - operationId
{
 "operationId": "O1sBc7PanPjB8tEadNC4t4GGPFM5kqC8yTKqwzHHV9q7FksuBoE"
}

Call a Smart Contract

This method is used to call smart contract.

Direction Type Format Example
Webpage to extension account.callSC
type: object
properties:
  nickname:
    type: string
    description: Account nickname to use to sign the operation.
  functionName:
    type: string
  at:
    type: string
  args:
    type: Args
    description: Smart contract arguments
  coins:
    type: string
    format: BigInt
    description: Amount of coins to send to the block creator
  nonPersistentExecution (optional):
    type: object
    properties:
      isNPE:
        type: boolean
        default: false
      maxGas:
        type: string
        format: BigInt
        default:"0"
    description: Setup a Non-persistent execution of the SC call
required:
  - nickname
  - functionName
  - at
  - args
  - coins
{
  "nickname": "my-account",
  "name": "register",
  "at": "AU19tCSKtiE4k9MJLyLH5sWGDZ7Rr2SiBf1ti3XqeCptwsXGvkef",
  "args": {
    "offset": 4,
    "serialized": [
      4,   0,   0,   0,
      116, 101, 115, 116
    ]
  },
  "coins": "2.0",
  "nonPersistentExecution": {
    "isNPE": true,
    "maxGas": "100000000"
  }
}
Extension to webpage account.callSC.response
type: object
properties:
  operationId:
    type: string
required:
  - operationId
{
 "operationId": "O1sBc7PanPjB8tEadNC4t4GGPFM5kqC8yTKqwzHHV9q7FksuBoE"
}

Transaction

Direction Type Format Example
Webpage to extension account.sendTransaction
type: object
properties:
  fee:
    type: string
    format: BigInt
  amount:
    type: string
    format: BigInt
  recipientAddress:
    type: string
required:
  - fee
  - amount
  - recipientAddress
{
 "fee": "12000000",
 "amount": "2000000000",
 "recipientAddress": "AU19tCSKtiE4k9MJLyLH5sWGDZ7Rr2SiBf1ti3XqeCptwsXGvkef"
}
Extension to webpage account.sendTransaction.response
type: object
properties:
  operationId:
    type: string
required:
  - operationId
{
 "operationId": "O1sBc7PanPjB8tEadNC4t4GGPFM5kqC8yTKqwzHHV9q7FksuBoE"
}

Node URLs

This method is used to get the node URLs known by the extension.

Direction Type Format Example
Webpage to extension Provider.getNodeUrls none null
Extension to webpage Provider.getNodeUrls.response
type: array
items:  
    type: string
    format: URL
[
  "http://localhost:1234", 
  "https://massa-nodes.net"
]

Generating New Accounts

This method will generate a new account upon request with a specified name defined by the user. The account shall be added to the wallet automatically.

Direction Type Format Example
Webpage to extension account.generateNewAccount
type: object
properties:
  name:
    type: string
{
 "name": "my-trading-account"
}
Extension to webpage account.generateNewAccount.response
type: object
properties:
  name:
    type: string
  address:
    type: string
required:
  - address
{
 "name": "my-trading-account",
 "address": "AU12..."
}

Security Considerations

  • The wallet provider must validate all inputs before performing any action.
  • The webpage must validate all input before processing any action.
  • The extension should ensure that communication is restricted to the intended parties and prevent any third-party intervention.
  • The wallet provider should be built with security as a primary concern, including measures such as encryption, authentication, and authorization to prevent unauthorized access or data breaches.

Implementation

The Massalabs implementation of this standard can be found on GitHub at https:/massalabs/wallet-provider.

Usage from a DApp point of view

Here's an example of how to use it:

import { providers } from "@massalabs/wallet-provider";

// Get all available Massa wallet providers.
const availableProviders = providers();

// Get a provider.
const myProvider = availableProviders[0];

// Import an account via the Massa wallet provider.
const privateKey = "Sxxxxxxxxxxxxxx";
const publicKey = "Pxxxxxxxxxxxxxxx";
await myProvider.importAccount(publicKey, privateKey);

// Get accounts.
const myAccounts = await myProvider.accounts();

// Get one account.
const myAccount = myAccounts[0];

// Get the account's address.
const accountAddress = myAccount.address();

// Get the account's balances.
const accountBalance = await myAccount.balance();

// Sign a message.
const signature = await myAccount.sign([0, 1, 2]);

// Delete an account.
await myProvider.importAccount(myAccount.address());

// Get nodes url
const urls = await myProvider.getNodeUrls();

// buy rolls
const buyOperationId = await myAccount.buyRolls("10", "12000000");

// sell rolls
const sellOperationId = await myAccount.sellRolls("3", "12000000");

// send transaction
const sendTransactionId = await myAccount.sendTransaction("2000000000", "AU19tCSKtiE4k9MJLyLH5sWGDZ7Rr2SiBf1ti3XqeCptwsXGvkef", "12000000");

// generate and add a new random account
const newAccountDetails = await myProvider.generateNewAccount("my-trading-account");