AEIP | Title | Author | Status | Type | Category | Created |
---|---|---|---|---|---|---|
4 |
Decentralized app / Wallet communication protocol |
Charly Caulet |
Final |
Standard Track |
Interface |
2022-11-29 |
AEIP-4 purpose is to define a communication protocol between decentralized applications and Archethic Wallet.
Communication protocols depend on the host operating system. Proposals are listed here.
Platform | Support |
---|---|
Mobile (Web/App) | ❌ |
MacOS/Windows/Linux (Web/App) | ✅ |
- Wallet app provides a Websocket server using the JSON-RPC 2.0.
- Runs as the native desktop application.
- Provides a notification zone icon. Can be automatically run on computer startup.
- Browser extension injects a client in web pages (like [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193 does).
- Extension proxies RPC to Wallet app RPC server.
- Can check Wallet app's RPC server readyness.
Requests are encapsulated in a JSON-RPC 2.0 message.
{
"id": Number, // a unique client-generated integer identifying the request
"jsonrpc": "2.0",
"method": String, // a string containing the method to be invoked
"params": {
"origin": {
"name": Number, // human readable identifier of the DApp
"url": String | undefined, // URL of the DApp
"logo": Base64 | undefined, // logo of the DApp
},
"version": 2, // Version of the DApp API
"payload": Object | undefined, // Method parameters
}
}
Success response :
{
"id": Number, // the request identifier
"jsonrpc": "2.0",
"result": Object, // result payload
}
{
"id": Number, // the request identifier
"jsonrpc": "2.0",
"error": {
"code": Number, // Error code
"message": String | undefined, // Error description
"data": Object | undefined, // Error data
},
}
Platform | Support |
---|---|
Mobile (Web/App) | ✅ |
MacOS (Web/App) | ✅ |
Windows/Linux | ❌ |
WalletApp handles DApp requests through an Https Deeplink endpoint.
DApp implements a callback deeplink to receive requests responses.
Deeplink messages are appended to the deeplink URI. Because of that, there are limitations in the available formatting.
To get it working, the message is encoded that way :
+--------------+ +------+ +--------+
method_payload : | Json Message |-> | gzip |-> | base64 |
+--------------+ +------+ +--------+
Deeplink Url : `<scheme>://<host>/<method_name>/<method_payload>`
Example :
+--------------------------+ +----------------------------------------------------------------+
method_payload : | { "param1" : "a_value" } |----> | "H4sIAAAAAAAAA6tWUCpILErMNVRSsFJQSowvS8wpTVVSqAUAhIgKchgAAAA=" |
+--------------------------+ +----------------------------------------------------------------+
Deeplink Url : `scheme://host/a_method/H4sIAAAAAAAAA6tWUCpILErMNVRSsFJQSowvS8wpTVVSqAUAhIgKchgAAAA=`
Requests payload are encapsulated in a JSON-RPC 2.0 message.
{
"id": Number, // An unique client-generated integer identifying the request
"replyUrl": String, // Deeplink URL to which send the invokation result. This should be a Deeplink URL handled by the DApp.
"params": {
"origin": {
"name": String, // Human readable identifier of the DApp
"url": String | undefined, // URL of the DApp
"logo": Base64 | undefined, // Logo of the DApp
},
"version": 2, // Version of the DApp API
"payload": Object, // Method parameters
}
}
{
"id": Number, // The request identifier
"result": Object, // Result payload
}
{
"id": Number, // The request identifier
"failure": {
"code": Number, // Error code
"message": String | undefined, // Error description
"data": Object | undefined, // Error data
},
}
sequenceDiagram
participant Dapp
participant Wallet
Dapp->>Wallet: FileShare(NFTFile + Tx)
Wallet->>Wallet: sign(Tx)
Wallet->>Blockchain: send(SignedTx)
Wallet->>Dapp: Deeplink(SignedTx.address)
This is a two steps operation :
- Share file to WalletApp using FileShare
- Once file is copied in the WalletApp documents directory, WalletApp triggers TxSignature Deeplink
FileShare : Requires a platform-specific implementation
- Web : https://developer.chrome.com/articles/web-share-target/ https://web.dev/patterns/files/receive-shared-files/
- Android/iOS : https://pub.dev/packages/receive_sharing_intent
There are two kinds of methods :
- one time call
- subscriptions.
Subscriptions won't be available on Deeplink channel because of technical limitations.
Asks the right to ????
{
"TTL": Number, // Duration of the permission, expressed in seconds.
}
// no payload in response success
Gets the endpoint URL used on AEWallet.
// no payload in request
{
"endpointUrl": String // Endpoint URL
}
Gets the services setup on AEWallet.
// No payload
{
"services": [
{
"name": String, // Service name
"genesisAddress": String, // Transaction chain genesis address
}
]
}
Signs and sends a transaction.
{
"service": String | undefined, // The emitting service name. I undefined, AEWallet will ask the user which account to use
"suffix": String | undefined, // Derivation path suffix.
"transaction": Object, // The transaction to send
}
{
"transactionAddress": String, // Sent transaction address.
"nbConfirmations": Number, // Number of received confirmations.
"maxConfirmations": Number, // Max number of confirmations.
}
Signs and sends a transaction.
{
"transactions": [
{
"service": String | undefined, // The emitting service name. I undefined, AEWallet will ask the user which account to use
"suffix": String | undefined, // Derivation path suffix.
"transaction": Object, // The transaction to send
}
]
}
{
"transactions": [
{
"transactionAddress": String, // Sent transaction address.
"nbConfirmations": Number, // Number of received confirmations.
"maxConfirmations": Number, // Max number of confirmations.
}
]
}
Signs data with a service private key. If derivation path suffix is set, the private key is derived from [service private key], and [service derivation path + suffix].
{
"service": String | undefined, // The emitting service name. I undefined, AEWallet will ask the user which account to use
"suffix": String | undefined, // Derivation path suffix.
"payload": Object, // The payload to sign
}
{
"signedPayload": String, // Signed payload
}
Decrypts data with a service private key. If derivation path suffix is set, the private key is derived from [service private key], and [service derivation path + suffix].
{
"service": String, // The service name.
"suffix": String | undefined, // Derivation path suffix.
"encryptedPayload": Object, // The encrypted payload
}
{
"clearPayload": String, // Decrypted payload
}
Subscribes to account changes.
{
"service": String // service name
}
{
"subscriptionId": Number // Subscription id (required to unsubscribe)
}
Method name : accountNotification
.
{
"subscriptionId": Number, // Subscription id
"account": { // Undefined if account is removed
"name": String, // account name
"balance": Number, // balance
"lastAddress": String, // last transaction address
}
}
Unsubscribes to account changes.
{
"subscriptionId": String // Subscription id.
}
// empty