Suppose you are the aepp and you want to communicate with a waellet. What
you do is pick an EventTarget (typically window), and listen to its
MessageEvents, via something like
window.addEventListener('message', my_listener);
You then communicate with the wallet by sending messages back over the
EventTarget. You should probably use the sidekick
library to do
this.
Keep in mind that these messages are not secret. Any browser extension or
foreign page script can intercept these messages. Imagine as an (imperfect)
analogy that you work in an office. Everyone's mail is dumped on the floor
in the middle of the office, and you are responsible for picking out which
letters are addressed to you. Anyone else can pick up letters addressed to
you, and read them.
This module defines the shape of the messages that are sent. There are several
layers to the onion, each corresponding to natural branch points in the
protocol.
Example
// this is a aepp-to-wallet call which is sent to `window` by say sidekick // layer 2 layer 3 layer 4 // EventData_A2W<RpcCall<"connection.open", Params_A2W_connection_open>>) // layer 2: who is the message for {type : "to_waellet", // layer 3: json rpc data : {jsonrpc : "2.0", id : "ske-connect-1", method : "connection.open", // layer 4: AWCP-specific semantics params : {name : "sidekick examples", version : 1}}}
// this is the associated wallet-to-aepp response which is sent to `window` // by Superhero // layer 2 layer 3 layer 4 // EventData_W2A<RpcResp_ok<"connection.open", Result_W2A_connection_open>>) // layer 2: who is the message for {type : "to_aepp", // layer 3: json rpc data : {jsonrpc : "2.0", id : "ske-connect-1", method : "connection.open", // layer 4: AWCP-specific semantics result : {id : "mnhmmkepfddpifjkamaligfeemcbhdne", name : "Superhero", networkId : "ae_mainnet", origin : "chrome-extension://mnhmmkepfddpifjkamaligfeemcbhdne", type : "extension"}}}
Example
// this is a waellet-to-aepp cast (RPC verbiage: "notification"). It does // not require a response.
// layer 2 layer 3 layer 4 // EventData_W2A<RpcCast<"connection.announcePresence", Params_W2A_connection_announcePresence>>) // layer 2: who is the message for {type : "to_aepp", // layer 3: json rpc data : {jsonrpc : "2.0", method : "connection.announcePresence", // layer 4: AWCP-specific semantics params : {id : "{aee9e933-52b6-410a-8c3f-99c6be596b4e}", name : "Superhero", networkId : "ae_mainnet", origin : "moz-extension://ee425d81-d5b2-44b6-9406-4da31b019e7c", type : "extension"}}}
This layer corresponds to the "am I supposed to pay attention to this
event?" branch point.
Those data structures mentioned above have two fields.
type is a string which is either "to_aepp" or "to_waellet"
data contains the next layer
typeEventData_W2A <textendsany> = {type : "to_aepp", data : t};
typeEventData_A2W <textendsany> = {type : "to_waellet", data : t};
We're at message_event.data.data. The idiom here is "JSON RPC", which
is sort of a poor man's HTTP.
In general, the wallet is the server and the aepp is the client.
If you are the aepp, usually you are handling a response to a request
you sent to the waellet. For instance, you formed a transaction and
sent it to the waellet to sign, and the waellet is sending you back
either the signed transaction or an error (e.g. user rejected the
transaction).
The exception to this pattern is the wallet notifying you that it
exists, which is the only time the waellet sends a request (a "cast", or
a "notification") to the aepp. In no event does the aepp send a
response to the waellet.
I am not 100% sure what RPC stands for, but it will be helpful to think
about it as "remote procedure call". More below. This layer roughly
corresponds to the "given that I am supposed to pay attention to this
event, what am I supposed to do with this information?"
All requests have a method field (a string) and a params field (an
object). There are two types of requests:
"casts" (the RPC standard calls these "notifications"). These do not
need a response. This is only used for the waellet announcing it
exists.
"calls". These have an id field, and get a response. These are
used when the aepp is requesting the waellet to do something. The
response will have the same id field and the same method field.
So far nothing we've talked about is specific to Aeternity, Vanillae,
JR, or sidekick. This fourth layer is the actual semantics of the
messaging protocol between the aepp and the waellet.
By analogy, the first two layers are developing something like TCP. The
third layer is developing HTTP. And this layer is the actual routing
table of your website, which carries with it the expected semantics of
how the website is supposed to behave.
This module DOES NOT exhaustively define all of the communication
protocol that occurs in the SDK, only the subset that I have encountered
in practice.
The first layer is defined by the runtime, not here. So we're starting with
layer 2.
I have adapted the verbiage here, borrowing from Erlang, to make a
distinction between
casts (RPC calls these "notifications"): these
do NOT have an id field
AND do NOT require a response.
calls: these
DO have an id field
ANDDO require a response.
The RpcCall and RpcResp data structures each have an id_n
type parameter.
The purpose of this is to notate (and possibly enforce) at the type level
the constraint that, given a call with say id = 7, the response must also
have id = 7.
AWCP: aepp-waellet communication protocol
Suppose you are the aepp and you want to communicate with a waellet. What you do is pick an
EventTarget(typicallywindow), and listen to itsMessageEvents, via something likeYou then communicate with the wallet by sending messages back over the
EventTarget. You should probably use the sidekick library to do this.Keep in mind that these messages are not secret. Any browser extension or foreign page script can intercept these messages. Imagine as an (imperfect) analogy that you work in an office. Everyone's mail is dumped on the floor in the middle of the office, and you are responsible for picking out which letters are addressed to you. Anyone else can pick up letters addressed to you, and read them.
This module defines the shape of the messages that are sent. There are several layers to the onion, each corresponding to natural branch points in the protocol.
Example
Example
The
MessageEventlayer. This is what is actually sent as an event. This is an opaque object that is built into every runtime's standard library: https://developer.mozilla.org/en-US/docs/Web/API/MessageEventThe
MessageEventhas a field calleddata, which corresponds to the next layer.The
EventDatalayer. This is what goes inmessage_event.data. The structure that goes in here is one ofThis layer corresponds to the "am I supposed to pay attention to this event?" branch point.
Those data structures mentioned above have two fields.
typeis a string which is either"to_aepp"or"to_waellet"datacontains the next layerWe're at
message_event.data.data. The idiom here is "JSON RPC", which is sort of a poor man's HTTP.In general, the wallet is the server and the aepp is the client.
If you are the aepp, usually you are handling a response to a request you sent to the waellet. For instance, you formed a transaction and sent it to the waellet to sign, and the waellet is sending you back either the signed transaction or an error (e.g. user rejected the transaction).
The exception to this pattern is the wallet notifying you that it exists, which is the only time the waellet sends a request (a "cast", or a "notification") to the aepp. In no event does the aepp send a response to the waellet.
I am not 100% sure what RPC stands for, but it will be helpful to think about it as "remote procedure call". More below. This layer roughly corresponds to the "given that I am supposed to pay attention to this event, what am I supposed to do with this information?"
All requests have a
methodfield (a string) and aparamsfield (an object). There are two types of requests:"casts" (the RPC standard calls these "notifications"). These do not need a response. This is only used for the waellet announcing it exists.
"calls". These have an
idfield, and get a response. These are used when the aepp is requesting the waellet to do something. The response will have the sameidfield and the samemethodfield.So far nothing we've talked about is specific to Aeternity, Vanillae, JR, or sidekick. This fourth layer is the actual semantics of the messaging protocol between the aepp and the waellet.
By analogy, the first two layers are developing something like TCP. The third layer is developing HTTP. And this layer is the actual routing table of your website, which carries with it the expected semantics of how the website is supposed to behave.
This module DOES NOT exhaustively define all of the communication protocol that occurs in the SDK, only the subset that I have encountered in practice.
The first layer is defined by the runtime, not here. So we're starting with layer 2.
Notes on JSON RPC 2.0
I have subtly changed the RPC protocol to
The only difference here is that the
paramsfield of requests is non-optional, and must be an object (the RPC standard allows arrays).Requests
I have adapted the verbiage here, borrowing from Erlang, to make a distinction between
casts (RPC calls these "notifications"): these
idfieldcalls: these
idfieldThe RpcCall and RpcResp data structures each have an
id_ntype parameter.The purpose of this is to notate (and possibly enforce) at the type level the constraint that, given a call with say
id = 7, the response must also haveid = 7.Links
MessageEvents: https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent