[wip] updating sidekick/jex/awcp for message sign demo
This commit is contained in:
+2
-489
@@ -1,490 +1,3 @@
|
||||
# Sidekick
|
||||
|
||||
Sidekick is a simple JavaScript library for talking to an Aeternity
|
||||
browser wallet extension such as Superhero from the document context
|
||||
of a webpage.
|
||||
|
||||
| **Thing** | **URL** |
|
||||
| --- | --- |
|
||||
| Bug Tracker | https://gitlab.com/DoctorAjayKumar/sidekick/-/issues |
|
||||
| Documentation | http://orangepill.healthcare/projects/sidekick/current/docs |
|
||||
| Examples | https://gitlab.com/DoctorAjayKumar/sidekick/-/tree/master/examples |
|
||||
| Git repository | https://gitlab.com/DoctorAjayKumar/sidekick |
|
||||
| Homepage | http://orangepill.healthcare/projects/sidekick |
|
||||
| License | ./LICENSE.txt |
|
||||
| Maintainer | Dr. Ajay Kumar PHD <DoctorAjayKumar@protonmail.com> |
|
||||
| Releases | http://orangepill.healthcare/projects/sidekick/releases |
|
||||
|
||||
|
||||
> One of the most important things for designing a computer, which I
|
||||
> think most designers don't do, is you study the problem you want to
|
||||
> solve. And then use what you learn from studying the problem you
|
||||
> want to solve to put in the mechanisms needed to solve it in the
|
||||
> computer you're building. No more, no less.
|
||||
|
||||
— Gerald Jay Sussman
|
||||
|
||||
### What sidekick is NOT
|
||||
|
||||
Sidekick is **not** ideal if you operate in the Node/NPM ecosystem. If you are
|
||||
working in the Node/NPM ecosystem, you probably want the [Aeternity JavaScript
|
||||
SDK](https://github.com/aeternity/aepp-sdk-js/). Every single
|
||||
Aeternity-related thing you could ever possibly want to do is possible to
|
||||
accomplish with the SDK.
|
||||
|
||||
All that Sidekick knows how to do is talk to a browser wallet extension. In an
|
||||
application, there is a great deal of necessary functionality (e.g. forming
|
||||
transactions for the wallet to sign) which sidekick assumes your server-side
|
||||
code has already handled.
|
||||
|
||||
In all software there is a tradeoff between simplicity and number of features.
|
||||
Sidekick is very simple: 2300 lines of TypeScript, including comments, with no
|
||||
dependencies. Therefore Sidekick intentionally only has a very limited set of
|
||||
features.
|
||||
|
||||
|
||||
# How to use this library
|
||||
|
||||
To obtain this library, download and unpack a release tarball
|
||||
|
||||
wget http://zxq9.com/projects/vanillae/sidekick/releases/sidekick_dist_X-Y-Z.tar.gz
|
||||
tar xzvf sidekick_dist_X-Y-Z.tar.gz
|
||||
|
||||
You *should* be able to get all of the functionality you need just
|
||||
from the exposed functions in the `sidekick` module. (If this is not
|
||||
the case, please report this as a bug.)
|
||||
|
||||
You probably want to read the documentation of:
|
||||
|
||||
sidekick.js : top-level function calls
|
||||
awcp/awcp.js : explains the return types of top-level sidekick
|
||||
functions, explains what sidekick actually does
|
||||
for you, and explains how all the various
|
||||
modules fit together
|
||||
skylight.js : main data structure in sidekick
|
||||
helpers.js : what it sounds like
|
||||
|
||||
There are examples explained in this document. Their source can be
|
||||
found in full in the GitLab repository, link above. The examples
|
||||
exhibit how to interact with the standard release tarball using
|
||||
TypeScript.
|
||||
|
||||
|
||||
## General flow of Sidekick usage
|
||||
|
||||
1. Import the sidekick.js file:
|
||||
|
||||
```typescript
|
||||
import * as sk from '/path/to/sidekick_dist_X-Y-Z/dist_js/sidekick.js'
|
||||
```
|
||||
|
||||
2. Create a `Skylight`. This is the main data structure of Sidekick.
|
||||
|
||||
There are two ways to do this
|
||||
|
||||
1. `start_dwim() : Promise<Skylight>`
|
||||
|
||||
This is probably the one you want. It starts up the Skylight
|
||||
and does the handshake with the wallet (will pop up the
|
||||
little window for your user). The effect of this is to
|
||||
populate all the variables like the user's public key.
|
||||
|
||||
Note that this is an async function. It doesn't return until
|
||||
the handshake is complete.
|
||||
|
||||
```typescript
|
||||
// Needs to be wrapped in an async function in order to use
|
||||
// await keyword
|
||||
async function main() : Promise<void> {
|
||||
let my_skylight = await sk.start_dwim();
|
||||
...
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
|
||||
2. `start : Skylight`
|
||||
|
||||
This is the vanilla option. All it does is create the
|
||||
Skylight object.
|
||||
|
||||
Beware that this does start up a message queue that passively
|
||||
listens for messages from the wallet.
|
||||
|
||||
|
||||
3. Pass the skylight in to various functions along with additional
|
||||
calldata generated by your server-side backend code.
|
||||
|
||||
|
||||
## Example 1: Hello World
|
||||
|
||||
The effect of this example is to print "hello world" to the JS
|
||||
console (Ctrl+Shift+C in most browsers). This example is included for
|
||||
debugging/mental-quicksand-escape purposes.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Hello world: Sidekick</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Sidekick Example: Hello World</h1>
|
||||
|
||||
<h4>Check the console</h4>
|
||||
<script type="module">
|
||||
import * as sk from '../sidekick_dist/dist_js/sidekick.js';
|
||||
sk.hello();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
The important lines are
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
import * as sk from '../sidekick_dist/dist_js/sidekick.js';
|
||||
sk.hello();
|
||||
</script>
|
||||
```
|
||||
|
||||
This shows
|
||||
|
||||
- you need to use `type="module"` in order for imports to work
|
||||
- how to do an import
|
||||
- how to call functions from an imported module
|
||||
|
||||
|
||||
## Example 2: get the user to sign a transaction
|
||||
|
||||
Anything you will want the user to do (e.g. sign smart contracts) is
|
||||
contained in "get the user to sign a transaction". This example
|
||||
illustrates the basic things you need to do in order to accomplish
|
||||
that.
|
||||
|
||||
Sidekick does not provide functionality for forming the transaction
|
||||
string you want the user to sign. Your backend should be doing that.
|
||||
|
||||
The example here uses a shim library called `parasite`, which you can find in
|
||||
the GitLab repository in the `examples` directory. Parasite is just a rewrite
|
||||
layer in front of the `testnet.aeternity.io` JSON interface. It is not suitable
|
||||
for usage in production.
|
||||
|
||||
This is adapted from
|
||||
[`examples/examples_src_ts/do_a_transfer.ts`][dat_ts]. The
|
||||
difference is that `do_a_transfer.ts` is written against
|
||||
[`examples/examples_html/do_a_transfer.html`][dat_html]. It contains
|
||||
a lot of unnecessary detail, like doing things in response to buttons
|
||||
being clicked, and filling in DOM textboxes with the results of things.
|
||||
|
||||
The full example has logic that resembles a register machine, rather
|
||||
than a functional program. This example is closer to a functional
|
||||
program.
|
||||
|
||||
[dat_html]: https://gitlab.com/DoctorAjayKumar/sidekick/-/blob/master/examples/examples_html/do_a_transfer.html
|
||||
[dat_ts]: https://gitlab.com/DoctorAjayKumar/sidekick/-/blob/master/examples/examples_src_ts/do_a_transfer.ts
|
||||
|
||||
|
||||
```typescript
|
||||
import * as ae_node from './parasite/ae_node.js';
|
||||
import * as sk from '../sidekick_dist/dist_js/sidekick.js';
|
||||
|
||||
async function
|
||||
main()
|
||||
: Promise<void>
|
||||
{
|
||||
//---------------------------------------------------------------
|
||||
// STEP 1: MAKE A SKYLIGHT
|
||||
//---------------------------------------------------------------
|
||||
let skl : sk.Skylight =
|
||||
await sk.start_dwim(sk.TIMEOUT_DEF_DETECT,
|
||||
sk.TIMEOUT_DEF_CONNECT,
|
||||
sk.TIMEOUT_DEF_ADDRESS);
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// STEP 2: GET THE USER'S ADDRESS
|
||||
//---------------------------------------------------------------
|
||||
let user_addr : string =
|
||||
await sk.address(skl, sk.TIMEOUT_DEF_ADDRESS);
|
||||
|
||||
// faster/non-async:
|
||||
//
|
||||
// let user_addr = skl.waellet_address
|
||||
//
|
||||
// the non-commented code
|
||||
//
|
||||
// - will crash if the wallet isn't connected
|
||||
// - will query the wallet for the address if the wallet *is*
|
||||
// connected but the skl.waellet_address field *is not*
|
||||
// populated
|
||||
// - will crash if that times out after the second argument
|
||||
// number of milliseconds
|
||||
//
|
||||
// if you know with 100% certainty that the address field will be
|
||||
// populated (which we do here, because we used connect_dwim),
|
||||
// then it's safe to just grab the field from the skylight
|
||||
//
|
||||
// The problem is that if the address field isn't populated, the
|
||||
// commented code defaults to `undefined`, rather than crashing
|
||||
// with an error message and callstack trace
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// STEP 3: FORM THE TRANSACTION
|
||||
//
|
||||
// This will be different for your application, because you're
|
||||
// not using parasite (right?)
|
||||
//
|
||||
// You need to figure out how to make something like `tx_obj` on
|
||||
// your own.
|
||||
//
|
||||
// `tx_obj` is just `{tx: "some_base58_garbage"}`.
|
||||
//---------------------------------------------------------------
|
||||
|
||||
let target_addr : string =
|
||||
'ak_dvNHMgVvdSgDchLsmcUpuFTbMBGfG3E5V9KZnNjLYPyEhcqnL';
|
||||
|
||||
// amount is in aettos
|
||||
let amount : number = 1;
|
||||
let endpoint : string = ae_node.URL_TESTNET;
|
||||
let spendtx = {'recipient_id' : target_addr,
|
||||
'amount' : amount,
|
||||
'fee' : ae_node.MIN_FEE,
|
||||
'sender_id' : user_addr,
|
||||
'payload' : ""};
|
||||
let tx_obj = await ae_node.PostSpend(endpoint, spendtx);
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// STEP 4: HAVE THE USER SIGN THE TRANSACTION
|
||||
//
|
||||
// There are two options:
|
||||
//
|
||||
// 1. `tx_sign_no_propagate` simply has the wallet sign the
|
||||
// transaction, and return the signed transaction back to you
|
||||
//
|
||||
// 2. `tx_sign_yes_propagate` has the wallet sign the transaction
|
||||
// and also propagates it into the network. It returns a more
|
||||
// elaborate data structure, which is documented in somewhere
|
||||
// the AWCP module documentation.
|
||||
//---------------------------------------------------------------
|
||||
|
||||
let result =
|
||||
await sk.tx_sign_no_propagate(skl,
|
||||
tx_obj,
|
||||
sk.NETWORK_ID_TESTNET,
|
||||
sk.TIMEOUT_DEF_SIGN);
|
||||
|
||||
console.log(result);
|
||||
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
|
||||
|
||||
# Known pitfalls
|
||||
|
||||
- See notes in sidekick module about potential state crossups if your
|
||||
document logic is multi-threaded.
|
||||
|
||||
- Has only been tested against Firefox-on-Linux
|
||||
|
||||
- Have not worked out good user idioms for handling errors
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# How the release is structured and why
|
||||
|
||||
The idea of the release is that it contains exactly what is needed in
|
||||
order to drop Sidekick into your project and start using it. No more,
|
||||
no less.
|
||||
|
||||
A release has the following structure:
|
||||
|
||||
```
|
||||
sidekick_dist_X-Y-Z/
|
||||
dist_js/............tsc-generated human-readable JS tree
|
||||
foo.js..............the actual code that is run
|
||||
foo.d.ts............included so that your TypeScript code can
|
||||
typecheck against sidekick
|
||||
foo.js.map..........debug symbols that map foo.js to
|
||||
locations in the TypeScript source
|
||||
src_ts/.............included so that the browser's debugger works
|
||||
foo.ts
|
||||
README.txt
|
||||
LICENSE.txt
|
||||
```
|
||||
|
||||
We use semantic versioning: `X.Y.Z`
|
||||
|
||||
- A change in `X` means an API-breaking change
|
||||
- A change in `Y` means an non-breaking API change
|
||||
- A change in `Z` means no change to the API
|
||||
|
||||
There is no documentation included in the release. There is
|
||||
autogenerated API documentation linked above, which is generated from
|
||||
this file and the sources that are included in the release.
|
||||
|
||||
|
||||
## What each file does
|
||||
|
||||
```
|
||||
src_ts/.....................TypeScript source for sidekick library
|
||||
awcp/.......................Aepp-Waellet Communication Protocol
|
||||
awcp.ts.....................Protocol definition
|
||||
msgq.ts.....................Block-on-raseev implementation
|
||||
msgr.ts.....................Protocol implementation
|
||||
helpers.ts..................what it sounds like
|
||||
sidekick.ts.................top-level module
|
||||
skylight.ts.................primary data structure
|
||||
```
|
||||
|
||||
|
||||
## Where is the NPM package or the webpack bundle?
|
||||
|
||||
There isn't one.
|
||||
|
||||
|
||||
### Why?
|
||||
|
||||
Sidekick is a library that deals with cryptocurrency. The security
|
||||
model is based on transparency and trust. A user must be able to
|
||||
inspect code that is running on his hardware handling his money.
|
||||
|
||||
We don't support NPM because of the security issues that NPM
|
||||
introduces. Briefly, the code can change at any time under the
|
||||
developer's nose without the developer knowing. The [leftpad
|
||||
debacle][lpad] and the [RIAEvangelist debacle][ria] are good examples
|
||||
of the types of vulnerabilities that package managers like NPM
|
||||
enable.
|
||||
|
||||
[lpad]: https://archive.ph/Qsh7j
|
||||
[ria]: https://archive.ph/OF5I9
|
||||
|
||||
We don't use something like webpack because that introduces an opaque
|
||||
rewrite using an untrusted tool. Webpack could plausibly alter the
|
||||
runtime behavior of the program, and we would have no way to detect
|
||||
that. Even if we trusted webpack, how do we obtain webpack? NPM.
|
||||
So.
|
||||
|
||||
It is debatable whether or not we should trust the TypeScript
|
||||
compiler (TSC). On the whole, TSC probably makes Sidekick more
|
||||
secure, simply by virtue of increasing overall code quality and
|
||||
eliminating the largest categories of potential bugs. Moreover, the
|
||||
output that TSC produces is human-readable, and has a very
|
||||
straightforward mapping to the original source code. A human can
|
||||
easily read the TSC-generated JavaScript tree, even without the aid
|
||||
of the source map, and have a high degree of faith that the code is
|
||||
trustworthy and that TSC is behaving as promised.
|
||||
|
||||
|
||||
|
||||
# How to obtain the source tree
|
||||
|
||||
The source tree is included in your release. You can clone the
|
||||
git repository with
|
||||
|
||||
```
|
||||
git clone https://gitlab.com/DoctorAjayKumar/sidekick.git
|
||||
```
|
||||
|
||||
|
||||
|
||||
# Repo file tree (non-exhaustive)
|
||||
|
||||
|
||||
```
|
||||
examples/...................Examples
|
||||
contract-examples/..........Example Sophia smart contracts
|
||||
examples_html/..............Low-budget example interfaces
|
||||
do_a_transfer.html......Send money to an arbitrary address
|
||||
hello.html..............Hello world
|
||||
ide.html................Play around with smart contracts
|
||||
examples_src_ts/............TypeScript source for each example
|
||||
parasite/...................JavaScript shim that does what
|
||||
your backend code ordinarily
|
||||
would do
|
||||
ae_compiler.ts..............Talk to a remote Sophia
|
||||
compiler JSON HTTP interface
|
||||
ae_node.ts..................Talk to a node (for forming
|
||||
transactions, querying chain,
|
||||
etc)
|
||||
net.ts......................Network helper functions
|
||||
do_a_transfer.ts........Send money to an arbitrary address
|
||||
ide.ts..................Play around with smart contracts
|
||||
tsconfig.json...............Examples-specific tsc configuration
|
||||
src_ts/.....................TypeScript source for sidekick library
|
||||
awcp/.......................Aepp-Waellet Communication Protocol
|
||||
awcp.ts.....................Protocol definition
|
||||
msgq.ts.....................Block-on-raseev implementation
|
||||
msgr.ts.....................Protocol implementation
|
||||
helpers.ts..................what it sounds like
|
||||
sidekick.ts.................top-level module
|
||||
skylight.ts.................primary data structure
|
||||
LICENSE.txt.................MIT License
|
||||
Makefile....................Makefile
|
||||
make........................Equivalent to `make build`
|
||||
make build..................Run tsc
|
||||
make clean..................rm -r dist_js sidekick_dist docs \
|
||||
examples/examples_dist_js
|
||||
make dist...................Build a release tarball directory
|
||||
make jsdoc..................Build the HTML documentation
|
||||
make build_examples.........cd examples && tsc
|
||||
make serve_examples.........cd examples && python3 -m \
|
||||
http.server 8001
|
||||
README.txt..................This file
|
||||
STYLE_GUIDE.md..............Explains why the code looks so weird
|
||||
TODO.md.....................what it sounds like
|
||||
tsconfig.json...............Configuration file for tsc
|
||||
```
|
||||
|
||||
|
||||
|
||||
# How to build a release
|
||||
|
||||
You will need the TypeScript compiler installed. See prereqs section.
|
||||
|
||||
make dist
|
||||
|
||||
|
||||
## Prereqs for building source files
|
||||
|
||||
If you want to edit and rebuild the source files, you need TypeScript
|
||||
installed, and you should update npm.
|
||||
|
||||
npm install -g typescript
|
||||
npm install -g npm
|
||||
|
||||
## Protip: avoid using `sudo npm install -g`
|
||||
|
||||
If you want to avoid using sudo
|
||||
|
||||
```
|
||||
mkdir ~/.npm-packages
|
||||
npm config set prefix "${HOME}/.npm-packages"
|
||||
```
|
||||
|
||||
Edit `~/.bashrc` or `~/.zshrc` with
|
||||
|
||||
```
|
||||
NPM_PACKAGES="${HOME}/.npm-packages"
|
||||
export PATH=$NPM_PACKAGES/bin:$PATH
|
||||
```
|
||||
|
||||
Source: https://github.com/sindresorhus/guides/blob/main/npm-global-without-sudo.md
|
||||
|
||||
|
||||
# How to build and view documentation
|
||||
|
||||
```
|
||||
make build_docs
|
||||
make serve_docs
|
||||
```
|
||||
|
||||
# awcp (package documentation)
|
||||
|
||||
Please click the word "(Documentation)" in the sidebar
|
||||
|
||||
+396
-18
@@ -10,12 +10,77 @@
|
||||
* ```
|
||||
*
|
||||
* You then communicate with the wallet by sending messages back over the
|
||||
* `EventTarget`
|
||||
* `EventTarget`. You should probably use the [sidekick
|
||||
* library](https://github.com/aeternity/Vanillae/tree/master/sidekick) 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
|
||||
* ```ts
|
||||
* // 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
|
||||
* ```ts
|
||||
* // 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"}}}
|
||||
* ```
|
||||
*
|
||||
*
|
||||
*
|
||||
* 1. The `MessageEvent` layer. 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/MessageEvent
|
||||
@@ -26,8 +91,8 @@
|
||||
* 2. The `EventData` layer. This is what goes in `message_event.data`. The
|
||||
* structure that goes in here is one of
|
||||
*
|
||||
* 1. `EventData_W2A`: waellet-to-aepp
|
||||
* 2. `EventData_A2W`: aepp-to-waellet
|
||||
* 1. {@link EventData_W2A}: waellet-to-aepp
|
||||
* 2. {@link EventData_A2W}: aepp-to-waellet
|
||||
*
|
||||
* This layer corresponds to the "am I supposed to pay attention to this
|
||||
* event?" branch point.
|
||||
@@ -108,7 +173,7 @@
|
||||
* 2. more accurately represent how it is used in practice
|
||||
*
|
||||
* The only difference here is that the `params` field of requests is
|
||||
* non-optional, and must be an object (the RPC standard allows arrays).
|
||||
* non-optional, and __must be an object__ (the RPC standard allows arrays).
|
||||
*
|
||||
* ## Requests
|
||||
*
|
||||
@@ -125,8 +190,8 @@
|
||||
* 1. __DO__ have an `id` field
|
||||
* 2. __AND__ __DO__ require a response.
|
||||
*
|
||||
* The `RpcCall` and `RpcResp` data structures each have an `id_n` type
|
||||
* parameter.
|
||||
* The {@link RpcCall} and {@link 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
|
||||
@@ -135,14 +200,14 @@
|
||||
* # Links
|
||||
*
|
||||
* - `MessageEvent`s: https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent
|
||||
* - JSON RPC 2.0 https://www.jsonrpc.org/specification
|
||||
* - JSON RPC 2.0: https://www.jsonrpc.org/specification
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
// TODONE: enumerate RPC errors
|
||||
// TODO: examples
|
||||
|
||||
// TODO: transaction.sign and propagate
|
||||
// TODO: constants for method names
|
||||
|
||||
|
||||
@@ -205,7 +270,7 @@ export {
|
||||
RpcCall_A2W_tx_sign_noprop,
|
||||
RpcResp_W2A_tx_sign_noprop,
|
||||
EventData_A2W_tx_sign_noprop,
|
||||
EventData_W2A_tx_sign_noprop
|
||||
EventData_W2A_tx_sign_noprop,
|
||||
// message.sign
|
||||
Params_A2W_msg_sign,
|
||||
Result_W2A_msg_sign,
|
||||
@@ -216,9 +281,6 @@ export {
|
||||
};
|
||||
|
||||
|
||||
// TODO: Give examples for everything
|
||||
// TODO: Add back in transaction.sign and propagate
|
||||
|
||||
//=============================================================================
|
||||
// LAYER 2: WHO IS THIS MESSAGE FOR
|
||||
//
|
||||
@@ -230,12 +292,16 @@ export {
|
||||
* This is the data that is sent from the wallet to the aepp through the Event
|
||||
* bus
|
||||
*
|
||||
* @example
|
||||
* (layer 2)
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // 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",
|
||||
@@ -256,10 +322,13 @@ type EventData_W2A
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // 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}}}
|
||||
* ```
|
||||
@@ -370,6 +439,8 @@ const ERROR_CODE_RpcMethodNotFoundError = -32601;
|
||||
/**
|
||||
* Error data inside the `error` field of a `RpcResp_Err`
|
||||
*
|
||||
* (layer 3)
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* {code : 4,
|
||||
@@ -394,10 +465,14 @@ type RpcError
|
||||
* the server that do not need a response. An example is the wallet announcing
|
||||
* it exists.
|
||||
*
|
||||
* (layer 3)
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* //layer 3: JSON RPC
|
||||
* {jsonrpc : "2.0",
|
||||
* method : "connection.announcePresence",
|
||||
* // layer 4: AWCP-specific semantics
|
||||
* params : {id : "{aee9e933-52b6-410a-8c3f-99c6be596b4e}",
|
||||
* name : "Superhero",
|
||||
* networkId : "ae_mainnet",
|
||||
@@ -413,10 +488,22 @@ type RpcCast
|
||||
params : params_t};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This type is used for requests from the client to the server that require a
|
||||
* response.
|
||||
*
|
||||
* (layer 3)
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // layer 3: json rpc
|
||||
* {jsonrpc : "2.0",
|
||||
* id : "ske-connect-1",
|
||||
* method : "connection.open",
|
||||
* // layer 4: AWCP-specific semantics
|
||||
* params : {name : "sidekick examples",
|
||||
* version : 1}}}
|
||||
* ```
|
||||
*/
|
||||
type RpcCall
|
||||
<method_s extends string,
|
||||
@@ -435,6 +522,24 @@ type RpcCall
|
||||
|
||||
/**
|
||||
* This is the shape of unsuccessful responses
|
||||
*
|
||||
* @example
|
||||
* From a page script, using sidekick, I asked for the user's address.
|
||||
* Superhero popped up the little dialog thing asking if I wanted to connect,
|
||||
* and I hit "deny". This is what was sent back. Code `4` corresponds to
|
||||
* {@link ERROR_CODE_RpcRejectedByUserError}.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // layer 3: RPC
|
||||
* {jsonrpc : "2.0",
|
||||
* id : "ske-address-1",
|
||||
* method : "address.subscribe",
|
||||
* // layer 4: AWCP semantics
|
||||
* error : {code : 4,
|
||||
* data : {},
|
||||
* message : "Operation rejected by user"}}
|
||||
* ```
|
||||
*/
|
||||
type RpcResp_error
|
||||
<method_s extends string>
|
||||
@@ -447,6 +552,20 @@ type RpcResp_error
|
||||
|
||||
/**
|
||||
* This is the shape of successful responses
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // layer 3: RPC
|
||||
* {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"}}
|
||||
* ```
|
||||
*/
|
||||
type RpcResp_ok
|
||||
<method_s extends string,
|
||||
@@ -460,6 +579,34 @@ type RpcResp_ok
|
||||
|
||||
/**
|
||||
* This is the shape of generic responses
|
||||
*
|
||||
* @example
|
||||
* Successful response ({@link RpcResp_ok})
|
||||
* ```ts
|
||||
* // layer 3: RPC
|
||||
* {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
|
||||
* Unsuccessful response ({@link RpcResp_error})
|
||||
* ```ts
|
||||
* // layer 3: RPC
|
||||
* {jsonrpc : "2.0",
|
||||
* id : "ske-address-1",
|
||||
* method : "address.subscribe",
|
||||
* // layer 4: AWCP semantics
|
||||
* error : {code : 4,
|
||||
* data : {},
|
||||
* message : "Operation rejected by user"}}
|
||||
* ```
|
||||
*/
|
||||
type RpcResp
|
||||
<method_s extends string,
|
||||
@@ -471,6 +618,34 @@ type RpcResp
|
||||
|
||||
/**
|
||||
* Most generic possible response
|
||||
*
|
||||
* @example
|
||||
* Successful response ({@link RpcResp_ok})
|
||||
* ```ts
|
||||
* // layer 3: RPC
|
||||
* {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
|
||||
* Unsuccessful response ({@link RpcResp_error})
|
||||
* ```ts
|
||||
* // layer 3: RPC
|
||||
* {jsonrpc : "2.0",
|
||||
* id : "ske-address-1",
|
||||
* method : "address.subscribe",
|
||||
* // layer 4: AWCP semantics
|
||||
* error : {code : 4,
|
||||
* data : {},
|
||||
* message : "Operation rejected by user"}}
|
||||
* ```
|
||||
*/
|
||||
type RpcResp_Any = RpcResp<string, any>;
|
||||
|
||||
@@ -487,9 +662,6 @@ type RpcResp_Any = RpcResp<string, any>;
|
||||
// use which I did not encounter in the wild.
|
||||
//=============================================================================
|
||||
|
||||
// TODO: need to do more experimentation with superhero to see what sorts of
|
||||
// messages it sends back to the other requests
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// connection.announcePresence
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -568,6 +740,12 @@ type EventData_W2A_connection_announcePresence
|
||||
* Parameters of aepp-to-waellet "connection.open" call
|
||||
*
|
||||
* (layer 4)
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* {name : "sidekick examples",
|
||||
* version : 1}
|
||||
* ```
|
||||
*/
|
||||
type Params_A2W_connection_open
|
||||
= {name : string,
|
||||
@@ -581,6 +759,15 @@ type Params_A2W_connection_open
|
||||
* Same as `Params_W2A_connection_open` empirically
|
||||
*
|
||||
* (layer 4)
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* {id : "{aee9e933-52b6-410a-8c3f-99c6be596b4e}",
|
||||
* name : "Superhero",
|
||||
* networkId : "ae_uat",
|
||||
* origin : "moz-extension://ee425d81-d5b2-44b6-9406-4da31b019e7c",
|
||||
* type : "extension"}
|
||||
* ```
|
||||
*/
|
||||
type Result_W2A_connection_open
|
||||
= Params_W2A_connection_announcePresence;
|
||||
@@ -591,6 +778,16 @@ type Result_W2A_connection_open
|
||||
* Shape of aepp-to-waellet "connection.open" RPC call
|
||||
*
|
||||
* (layer 3)
|
||||
* @example
|
||||
* ```ts
|
||||
* // layer 3: json rpc
|
||||
* {jsonrpc : "2.0",
|
||||
* id : "ske-connect-1",
|
||||
* method : "connection.open",
|
||||
* // layer 4: AWCP-specific semantics
|
||||
* params : {name : "sidekick examples",
|
||||
* version : 1}}}
|
||||
* ```
|
||||
*/
|
||||
type RpcCall_A2W_connection_open
|
||||
= RpcCall<"connection.open",
|
||||
@@ -602,6 +799,22 @@ type RpcCall_A2W_connection_open
|
||||
* Shape of waellet-to-aepp "connection.open" RPC response
|
||||
*
|
||||
* (layer 3)
|
||||
*
|
||||
* @example
|
||||
* ```json
|
||||
* {
|
||||
* "jsonrpc": "2.0",
|
||||
* "id": "ske-connect-1",
|
||||
* "method": "connection.open",
|
||||
* "result": {
|
||||
* "id": "mnhmmkepfddpifjkamaligfeemcbhdne",
|
||||
* "name": "Superhero",
|
||||
* "networkId": "ae_mainnet",
|
||||
* "origin": "chrome-extension://mnhmmkepfddpifjkamaligfeemcbhdne",
|
||||
* "type": "extension"
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
type RpcResp_W2A_connection_open
|
||||
= RpcResp<"connection.open",
|
||||
@@ -613,6 +826,22 @@ type RpcResp_W2A_connection_open
|
||||
* The actual aepp-to-waellet "connection.open" event data passed over the message bus
|
||||
*
|
||||
* (layer 2)
|
||||
*
|
||||
* @example
|
||||
* ```json
|
||||
* {
|
||||
* "type": "to_waellet",
|
||||
* "data": {
|
||||
* "jsonrpc": "2.0",
|
||||
* "id": "ske-connect-1",
|
||||
* "method": "connection.open",
|
||||
* "params": {
|
||||
* "name": "sidekick examples",
|
||||
* "version": 1
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
type EventData_A2W_connection_open
|
||||
= EventData_A2W<RpcCall_A2W_connection_open>;
|
||||
@@ -623,6 +852,24 @@ type EventData_A2W_connection_open
|
||||
* The actual waellet-to-aepp "connection.open" event data passed over the message bus
|
||||
*
|
||||
* (layer 2)
|
||||
*
|
||||
* @example
|
||||
* ```json
|
||||
* {
|
||||
* "type": "to_aepp",
|
||||
* "data": {
|
||||
* "jsonrpc": "2.0",
|
||||
* "id": "ske-connect-1",
|
||||
* "method": "connection.open",
|
||||
* "result": {
|
||||
* "id": "mnhmmkepfddpifjkamaligfeemcbhdne",
|
||||
* "name": "Superhero",
|
||||
* "networkId": "ae_mainnet",
|
||||
* "origin": "chrome-extension://mnhmmkepfddpifjkamaligfeemcbhdne",
|
||||
* "type": "extension"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
type EventData_W2A_connection_open
|
||||
= EventData_W2A<RpcResp_W2A_connection_open>;
|
||||
@@ -638,6 +885,14 @@ type EventData_W2A_connection_open
|
||||
* Parameter type of aepp-to-waellet "address.subscribe" call
|
||||
*
|
||||
* (layer 4)
|
||||
*
|
||||
* @example
|
||||
* ```json
|
||||
* {
|
||||
* "type": "subscribe",
|
||||
* "value": "connected"
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
type Params_A2W_address_subscribe
|
||||
= {type : "subscribe",
|
||||
@@ -651,11 +906,33 @@ type Params_A2W_address_subscribe
|
||||
* (layer 4)
|
||||
*
|
||||
* @example
|
||||
* This is if the user only has a single keypair
|
||||
* ```typescript
|
||||
* {subscription : ["connected"],
|
||||
* address : {current : {"ak_2Wsa8iAmAm917evwDEZjouvPUXKx2nUv5Uz8e8oNXTDfDXnMRN": {}},
|
||||
* connected : {}}}
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* This is if the user has many keypairs. The currently selected one is under
|
||||
* `current`. Craig, I agree this is stupid, but that's how it works.
|
||||
* ```json
|
||||
* {
|
||||
* "subscription": [
|
||||
* "connected"
|
||||
* ],
|
||||
* "address": {
|
||||
* "current": {
|
||||
* "ak_25C3xaAGQddyKAnaLLMjAhX24xMktH2NNZxY3fMaZQLMGED2Nf": {}
|
||||
* },
|
||||
* "connected": {
|
||||
* "ak_BMtPGuqDhWLnMVL4t6VFfS32y2hd8TSYwiYa2Z3VdmGzgNtJP": {},
|
||||
* "ak_25BqQuiVCasiqTkXHEffq7XCsuYEtgjNeZFeVFbuRtJkfC9NyX": {},
|
||||
* "ak_4p6gGoCcwQzLXd88KhdjRWYgd4MfTsaCeD8f99pzZhJ6vzYYV": {}
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
type Result_W2A_address_subscribe
|
||||
= {subscription : ["connected"],
|
||||
@@ -668,6 +945,19 @@ type Result_W2A_address_subscribe
|
||||
* Shape of aepp-to-waellet "address.subscribe" RPC call
|
||||
*
|
||||
* (layer 3)
|
||||
*
|
||||
* @example
|
||||
* ```json
|
||||
* {
|
||||
* "jsonrpc": "2.0",
|
||||
* "id": "ske-address-1",
|
||||
* "method": "address.subscribe",
|
||||
* "params": {
|
||||
* "type": "subscribe",
|
||||
* "value": "connected"
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
type RpcCall_A2W_address_subscribe
|
||||
= RpcCall<"address.subscribe",
|
||||
@@ -679,6 +969,52 @@ type RpcCall_A2W_address_subscribe
|
||||
* Result of waellet-to-aepp "address.subscribe" response
|
||||
*
|
||||
* (layer 3)
|
||||
*
|
||||
* @example
|
||||
* Case where the wallet only has one keypair
|
||||
* ```json
|
||||
* {
|
||||
* "jsonrpc": "2.0",
|
||||
* "id": "ske-address-1",
|
||||
* "method": "address.subscribe",
|
||||
* "result": {
|
||||
* "subscription": [
|
||||
* "connected"
|
||||
* ],
|
||||
* "address": {
|
||||
* "current": {
|
||||
* "ak_BMtPGuqDhWLnMVL4t6VFfS32y2hd8TSYwiYa2Z3VdmGzgNtJP": {}
|
||||
* },
|
||||
* "connected": {}
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* Case of many keypairs
|
||||
* ```json
|
||||
* {
|
||||
* "jsonrpc": "2.0",
|
||||
* "id": "ske-address-1",
|
||||
* "method": "address.subscribe",
|
||||
* "result": {
|
||||
* "subscription": [
|
||||
* "connected"
|
||||
* ],
|
||||
* "address": {
|
||||
* "current": {
|
||||
* "ak_25C3xaAGQddyKAnaLLMjAhX24xMktH2NNZxY3fMaZQLMGED2Nf": {}
|
||||
* },
|
||||
* "connected": {
|
||||
* "ak_BMtPGuqDhWLnMVL4t6VFfS32y2hd8TSYwiYa2Z3VdmGzgNtJP": {},
|
||||
* "ak_25BqQuiVCasiqTkXHEffq7XCsuYEtgjNeZFeVFbuRtJkfC9NyX": {},
|
||||
* "ak_4p6gGoCcwQzLXd88KhdjRWYgd4MfTsaCeD8f99pzZhJ6vzYYV": {}
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
type RpcResp_W2A_address_subscribe
|
||||
= RpcResp<"address.subscribe",
|
||||
@@ -690,6 +1026,22 @@ type RpcResp_W2A_address_subscribe
|
||||
* Actual aepp-to-waellet "address.subscribe" event data sent over the message bus
|
||||
*
|
||||
* (layer 2)
|
||||
*
|
||||
* @example
|
||||
* ```json
|
||||
* {
|
||||
* "type": "to_waellet",
|
||||
* "data": {
|
||||
* "jsonrpc": "2.0",
|
||||
* "id": "ske-address-1",
|
||||
* "method": "address.subscribe",
|
||||
* "params": {
|
||||
* "type": "subscribe",
|
||||
* "value": "connected"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
type EventData_A2W_address_subscribe
|
||||
= EventData_A2W<RpcCall_A2W_address_subscribe>;
|
||||
@@ -700,6 +1052,30 @@ type EventData_A2W_address_subscribe
|
||||
* Actual waellet-to-aepp "address.subscribe" event data sent over the message bus
|
||||
*
|
||||
* (layer 2)
|
||||
*
|
||||
* @example
|
||||
* This is the case where the wallet only has one keypair:
|
||||
* ```json
|
||||
* {
|
||||
* "type": "to_aepp",
|
||||
* "data": {
|
||||
* "jsonrpc": "2.0",
|
||||
* "id": "ske-address-1",
|
||||
* "method": "address.subscribe",
|
||||
* "result": {
|
||||
* "subscription": [
|
||||
* "connected"
|
||||
* ],
|
||||
* "address": {
|
||||
* "current": {
|
||||
* "ak_BMtPGuqDhWLnMVL4t6VFfS32y2hd8TSYwiYa2Z3VdmGzgNtJP": {}
|
||||
* },
|
||||
* "connected": {}
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
type EventData_W2A_address_subscribe
|
||||
= EventData_W2A<RpcResp_W2A_address_subscribe>;
|
||||
@@ -713,6 +1089,8 @@ type EventData_W2A_address_subscribe
|
||||
/**
|
||||
* Parameters for "transaction.sign" (do not propagate)
|
||||
*
|
||||
* If `returnSigned` is `false`, then Superhero will propagate the transaction.
|
||||
*
|
||||
* (layer 4)
|
||||
*/
|
||||
type Params_A2W_tx_sign_noprop
|
||||
@@ -809,7 +1187,7 @@ type Result_W2A_msg_sign
|
||||
* (layer 3)
|
||||
*/
|
||||
type RpcCall_A2W_msg_sign
|
||||
= RpcCall<"message.sign"
|
||||
= RpcCall<"message.sign",
|
||||
Params_A2W_msg_sign>;
|
||||
|
||||
|
||||
|
||||
+1
-71
@@ -1,73 +1,3 @@
|
||||
# Sidekick
|
||||
|
||||
Sidekick is a simple JavaScript library for talking to an Aeternity
|
||||
browser wallet extension such as Jaeck Russell or Superhero from the document
|
||||
context of a webpage.
|
||||
|
||||
# Build Prereqs
|
||||
|
||||
Assuming Ubuntu 18.04. Adapt these instructions for your own system.
|
||||
|
||||
You need
|
||||
|
||||
- `npm` to build TypeScript (and TypeDoc if you want to build the
|
||||
documentation)
|
||||
- `tsc` to compile
|
||||
- [jex](../utils/jex/) to facilitate the build
|
||||
|
||||
Steps:
|
||||
|
||||
sudo snap refresh
|
||||
sudo snap install node --channel 18/stable
|
||||
npm install -g typescript
|
||||
|
||||
## Protip: avoid using `sudo npm install -g`
|
||||
|
||||
If you want to avoid using sudo
|
||||
|
||||
```
|
||||
mkdir ~/.npm-packages
|
||||
npm config set prefix "${HOME}/.npm-packages"
|
||||
```
|
||||
|
||||
Edit `~/.bashrc` or `~/.zshrc` with
|
||||
|
||||
```
|
||||
NPM_PACKAGES="${HOME}/.npm-packages"
|
||||
export PATH=$NPM_PACKAGES/bin:$PATH
|
||||
```
|
||||
|
||||
# Build Steps
|
||||
|
||||
## 1. Build dependencies
|
||||
|
||||
The examples require `parasite` as a dependency, but sidekick itself does not.
|
||||
|
||||
```
|
||||
~/src/vanillae $ cd libs/awcp
|
||||
~/src/vanillae/libs/awcp $ jex dwim+
|
||||
~/src/vanillae/libs/awcp $ cd ../parasite
|
||||
~/src/vanillae/libs/parasite $ jex dwim+
|
||||
```
|
||||
|
||||
## 2. Build sidekick
|
||||
|
||||
```
|
||||
~/src/vanillae/libs/parasite $ cd ../../sidekick
|
||||
~/src/vanillae/sidekick $ jex dwim+
|
||||
```
|
||||
|
||||
## 3. Build examples
|
||||
|
||||
Note the `-`, not the `+`. The difference is that `-` just builds the project,
|
||||
but does not package it.
|
||||
|
||||
```
|
||||
~/src/vanillae/sidekick $ cd examples
|
||||
~/src/vanillae/sidekick/examples $ jex dwim-
|
||||
~/src/vanillae/sidekick/examples $ python3 -m http.server 8000
|
||||
```
|
||||
|
||||
Navigate to `http://localhost:8000/` in your browser to see the examples
|
||||
|
||||
The examples are the best documentation, for now.
|
||||
Click "sidekick" in the sidebar or hamburger menu
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
{realm, local}.
|
||||
{name, sidekick}.
|
||||
{version, "0.2.0"}.
|
||||
{deps, ["local-awcp-0.1.0"]}.
|
||||
{deps, ["local-awcp-0.2.0"]}.
|
||||
|
||||
+16
-109
@@ -1,124 +1,31 @@
|
||||
// tomorrow:
|
||||
// message signing
|
||||
// examples
|
||||
// documentation
|
||||
// project organization
|
||||
|
||||
/**
|
||||
* # How to use this library
|
||||
* # tl;dr
|
||||
*
|
||||
* This is a library for communicating with a browser wallet extension such as
|
||||
* Superhero
|
||||
* 1. {@link detect} the wallet
|
||||
* 2. {@link connect} to the wallet
|
||||
* 3. Get the wallet's {@link address}
|
||||
*
|
||||
* ## Step 0: Include `sidekick`
|
||||
* From there you can do one of two things
|
||||
*
|
||||
* ```
|
||||
* import * as sk from './path/to/sidekick.js';
|
||||
* ```
|
||||
* 1. Have the wallet sign transactions ({@link tx_sign_noprop})
|
||||
* 2. Have the wallet sign arbitrary messages ({@link msg_sign})
|
||||
*
|
||||
* ## Step 1: Make a `Logger`
|
||||
*
|
||||
* All of the entrypoints in sidekick require passing in a `Logger`. The idea
|
||||
* is that you can pass in custom logging hooks to log potential errors.
|
||||
*
|
||||
* There are two built-in loggers exported by this module:
|
||||
*
|
||||
* 1. `let my_logger = sk.wsl();`: does nothing
|
||||
* 2. `let my_logger = sk.cl();`: console logger
|
||||
* 3. `let my_logger = new sk.HttpLogger('https://foo.bar/baz')`: sends JSON to
|
||||
* the given endpoint in a POST request, in the following form
|
||||
*
|
||||
* ```
|
||||
* {level : 'debug' | 'info' | 'warning' | 'error',
|
||||
* message : string,
|
||||
* data : object}
|
||||
* ```
|
||||
* 4. `let my_logger = new sk.SeqLogger([my_logger1, my_logger2]);`: a helper
|
||||
* for composing several loggers sequentially.
|
||||
* 5. You can define anything that satisfies the `Logger` interface and pass
|
||||
* that in instead.
|
||||
*
|
||||
* ```
|
||||
* interface Logger {
|
||||
* debug : (message : string, data : object) => Promise<void>;
|
||||
* info : (message : string, data : object) => Promise<void>;
|
||||
* warning : (message : string, data : object) => Promise<void>;
|
||||
* error : (message : string, data : object) => Promise<void>;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ## Step 2: Detect the wallet
|
||||
*
|
||||
* ```
|
||||
* // timeout error message logger
|
||||
* let maybe_detected = await sk.detect(sk.TIMEOUT_DEF_DETECT, 'detect: timeout', my_logger);
|
||||
* ```
|
||||
*
|
||||
* Function:
|
||||
*
|
||||
* ```
|
||||
* async function
|
||||
* detect
|
||||
* (timeout_ms : number,
|
||||
* timeout_msg : string,
|
||||
* logger : Logger)
|
||||
* : Promise<Safe<awcp.Params_W2A_connection_announcePresence, SkTimeoutError>>
|
||||
* ```
|
||||
*
|
||||
* This returns some garbage that doesn't matter in a `Safe` type.
|
||||
* The `Safe` type does matter
|
||||
*
|
||||
* ```
|
||||
* type Safe<ok_t, err_t>
|
||||
* = Ok<ok_t>
|
||||
* | Error<err_t>;
|
||||
*
|
||||
* type Ok<ok_t>
|
||||
* = {ok : true,
|
||||
* result : ok_t};
|
||||
*
|
||||
* type Error<err_t>
|
||||
* = {ok : false,
|
||||
* error : err_t};
|
||||
* ```
|
||||
*
|
||||
* The motivation here is that when talking to the wallet, there are many
|
||||
* possible sources of errors. For instance, if you ask the wallet to sign a
|
||||
* transaction, the transaction might be malformed, maybe the user declines,
|
||||
* maybe it times out, whatever. All you care about is "did it work?" and you
|
||||
* don't want to deal with try/catch bullshit.
|
||||
*
|
||||
* The most straightforward way to extract the return value is with branching:
|
||||
*
|
||||
* ```
|
||||
* if (maybe_detected.ok) {
|
||||
* // ok is true in this case, so the field `result` exists
|
||||
* let awcp_crap = maybe_detected.result;
|
||||
* }
|
||||
* else {
|
||||
* // ok is false in this case, so the field `error` exists
|
||||
* let the_error = maybe_detected.error;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ## Step 3: Connect to the wallet
|
||||
*
|
||||
*
|
||||
*
|
||||
* ## Step 4: Get the user address
|
||||
*
|
||||
* ## Step 5: Sign a transaction
|
||||
* Forming the transactions and propagating them into the network is your
|
||||
* problem.
|
||||
*
|
||||
* You need a {@link Logger} for most calls. Probably you want {@link cl}. You
|
||||
* can write your own if you want but why would you complicate your life like
|
||||
* that.
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
// TODONE: add standardized logging interface
|
||||
// TODONE: logging hooks
|
||||
// TODO: invoice
|
||||
// TODO: get connect done
|
||||
// TODO: make the message queue for responses a map, fill the message queue properly
|
||||
// TODO: get it working with superhero
|
||||
// TODONE: invoice
|
||||
// TODONE: get connect done
|
||||
// TODONE: make the message queue for responses a map, fill the message queue properly
|
||||
// TODONE: get it working with superhero
|
||||
// TODO: jrx
|
||||
|
||||
// like: console, http, etc
|
||||
|
||||
@@ -38,6 +38,8 @@ help() ->
|
||||
% TODONE: jex fulldist
|
||||
% TODONE: jex install
|
||||
% TODO: jex get_mindist PKG
|
||||
% TODO: jex dwim++ = install
|
||||
% TODO: --name option for docs
|
||||
|
||||
% TODO: use less than full qualified names (not priority)
|
||||
% TODO: make fulldist for arbitrary installed package (requires storing jex.eterms, not hard but also not a priority)
|
||||
@@ -55,8 +57,9 @@ help_screen() ->
|
||||
" dwim- build project but don't make a release (init, pull, build)\n"
|
||||
" dwim+ build and make a minimal release (init, pull, build, mindist, push)\n"
|
||||
" dwim++ build and make a full release (init, pull, build, mindist, push, mkdocs, pushdocs)\n"
|
||||
" ls list installed packages\n"
|
||||
" install synonym for dwim++\n"
|
||||
" install [TARBALL_PATH] install the given package\n"
|
||||
" ls list installed packages\n"
|
||||
" viewdocs [PKG [PORT]] view package docs for PKG in browser\n"
|
||||
" get_mindist [PKG] get the mindist tarball for an installed package\n"
|
||||
"\n"
|
||||
@@ -99,8 +102,9 @@ help_screen() ->
|
||||
dispatch(["dwim-"]) -> dwim(minus);
|
||||
dispatch(["dwim+"]) -> dwim(plus);
|
||||
dispatch(["dwim++"]) -> dwim(plus_plus);
|
||||
dispatch(["ls"]) -> ls();
|
||||
dispatch(["install"]) -> install();
|
||||
dispatch(["install", Path]) -> install(Path);
|
||||
dispatch(["ls"]) -> ls();
|
||||
dispatch(["viewdocs"]) -> viewdocs();
|
||||
dispatch(["viewdocs", Pkg]) -> viewdocs(Pkg);
|
||||
dispatch(["viewdocs", Pkg, Port]) -> viewdocs(Pkg, Port);
|
||||
@@ -567,6 +571,9 @@ srsly_readme_path() ->
|
||||
%% jex install TARBALL_PATH
|
||||
%%-----------------------------------------------------------------------------
|
||||
|
||||
install() ->
|
||||
dwim(plus_plus).
|
||||
|
||||
install(TarballPath) ->
|
||||
case file_exists(TarballPath) of
|
||||
false -> error({file_dne, TarballPath});
|
||||
|
||||
Reference in New Issue
Block a user