Pharpend/develop (#1)

* move stuff in here

* reorganizing because zx needs to feel special

* add base58/base64 explainer draft
This commit is contained in:
pharpend
2022-10-05 23:30:49 +09:00
committed by GitHub
parent 5a6a6fb6c3
commit cbb117b504
86 changed files with 8188 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
*.swp
*.swo
*.beam
dist_js
sidekick_dist
docs
examples/examples_dist_js
www_tree
prose
*.hi
*.o
sidekick_mindist
sidekick_fulldist
dist
jx_mindist
*jx_include*
jex_mindist
erl_crash.dump
+16
View File
@@ -0,0 +1,16 @@
ISC License
Copyright (c) 2022 Peter Harpending
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
+15
View File
@@ -0,0 +1,15 @@
all: prepare build
deploy: prepare build mindist push
prepare:
jx pull -f
build:
jx build -f
mindist:
jx mindist -f
push:
jx push -f
+490
View File
@@ -0,0 +1,490 @@
# 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
```
+5
View File
@@ -0,0 +1,5 @@
{type, library}.
{realm, local}.
{name, awcp}.
{version, "0.1.0"}.
{deps, []}.
+724
View File
@@ -0,0 +1,724 @@
/**
* # 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` (typically `window`), and listen to its
* `MessageEvent`s, via something like
*
* ```typescript
* window.addEventListener('message', my_listener);
* ```
*
* You then communicate with the wallet by sending messages back over the
* `EventTarget`
*
* 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.
*
* 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
*
* The `MessageEvent` has a field called `data`, which corresponds to the
* next layer.
*
* 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
*
* This layer corresponds to the "am I supposed to pay attention to this
* event?" branch point.
*
* Those data structures mentioned above have two fields.
*
* 1. `type` is a string which is either `"to_aepp"` or `"to_waellet"`
* 2. `data` contains the next layer
*
* ```typescript
* type EventData_W2A
* <t extends any>
* = {type : "to_aepp",
* data : t};
* ```
*
* ``` typescript
* type EventData_A2W
* <t extends any>
* = {type : "to_waellet",
* data : t};
* ```
*
*
* 3. 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:
*
* 1. "casts" (the RPC standard calls these "notifications"). These do not
* need a response. This is only used for the waellet announcing it
* exists.
*
* 2. "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.
*
* 4. 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
*
* 1. improve it in such a way that it is easier to use in code
* 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).
*
* ## Requests
*
* I have adapted the verbiage here, borrowing from Erlang, to make a
* distinction between
*
* 1. casts (RPC calls these "notifications"): these
*
* 1. do __NOT__ have an `id` field
* 2. __AND__ do __NOT__ require a response.
*
* 2. calls: these
*
* 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 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`.
*
* # Links
*
* - `MessageEvent`s: https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent
* - JSON RPC 2.0 https://www.jsonrpc.org/specification
*
* @module
*/
// TODO: TS code style guide
// TODO: annotate everything with examples
// TODONE: enumerate RPC errors
// TODO: move Safe in here
// TODO: constants for method names
//=============================================================================
// IMPORTS
//=============================================================================
//import type {
// ERROR_TYPE_RpcInvalidTransactionError,
// ERROR_TYPE_RpcBroadcastError,
// ERROR_TYPE_RpcRejectedByUserError,
// ERROR_TYPE_RpcUnsupportedProtocolError,
// ERROR_TYPE_RpcConnectionDenyError,
// ERROR_TYPE_RpcNotAuthorizeError,
// ERROR_TYPE_RpcPermissionDenyError,
// ERROR_TYPE_RpcInternalError,
// ERROR_TYPE_RpcMethodNotFoundError,
//} from './errcode.js';
//=============================================================================
// EXPORTS
//=============================================================================
export {
// error code constants
ERROR_CODE_RpcInvalidTransactionError,
ERROR_CODE_RpcBroadcastError,
ERROR_CODE_RpcRejectedByUserError,
ERROR_CODE_RpcUnsupportedProtocolError,
ERROR_CODE_RpcConnectionDenyError,
ERROR_CODE_RpcNotAuthorizeError,
ERROR_CODE_RpcPermissionDenyError,
ERROR_CODE_RpcInternalError,
ERROR_CODE_RpcMethodNotFoundError,
// Layer 2: events
EventData_W2A,
EventData_A2W,
// Layer 3: RPC
RpcError,
RpcCast,
RpcCall,
RpcResp_error,
RpcResp_ok,
RpcResp,
RpcResp_Any,
// Layer 4: specific semantics
// connection.announcePresence
Params_W2A_connection_announcePresence,
RpcCast_W2A_connection_announcePresence,
EventData_W2A_connection_announcePresence,
// connection.open
Params_A2W_connection_open,
Result_W2A_connection_open,
RpcCall_A2W_connection_open,
RpcResp_W2A_connection_open,
EventData_A2W_connection_open,
EventData_W2A_connection_open,
// address.subscribe
Params_A2W_address_subscribe,
Result_W2A_address_subscribe,
RpcCall_A2W_address_subscribe,
RpcResp_W2A_address_subscribe,
EventData_A2W_address_subscribe,
EventData_W2A_address_subscribe,
//// transaction.sign (propagate)
//Params_A2W_tx_sign_yesprop,
//Result_W2A_tx_sign_yesprop,
//RpcCall_A2W_tx_sign_yesprop,
//RpcResp_W2A_tx_sign_yesprop,
//EventData_A2W_tx_sign_yesprop,
//EventData_W2A_tx_sign_yesprop,
// transaction.sign (do not propagate)
Params_A2W_tx_sign_noprop,
Result_W2A_tx_sign_noprop,
RpcCall_A2W_tx_sign_noprop,
RpcResp_W2A_tx_sign_noprop,
EventData_A2W_tx_sign_noprop,
EventData_W2A_tx_sign_noprop
};
//=============================================================================
// LAYER 2: WHO IS THIS MESSAGE FOR
//
// The first layer is defined by the runtime, not here. So we're starting with
// layer 2.
//=============================================================================
/**
* This is the data that is sent from the wallet to the aepp through the Event
* bus
*/
type EventData_W2A
<t extends any>
= {type : "to_aepp",
data : t};
/**
* This is the data that is sent from the aepp to the wallet through the Event
* bus
*/
type EventData_A2W
<t extends any>
= {type : "to_waellet",
data : t};
//=============================================================================
// LAYER 3: JSON RPC 2.0
//
// It should be noted in general that the id field exists as a type parameter
// so that we can use the type system to denote ID matches in callbacks.
//
// Remember, in TypeScript's type system, values are valid types.
//
// So `(_arg0: Foo<3, string>) => Bar<3, number>` is a valid type
//=============================================================================
/**
* `const ERROR_CODE_RpcInvalidTransactionError = 2;`
*
* See https://github.com/aeternity/aepp-sdk-js/blob/1065da9a46b8dbfe60a2c3e5646e7422ee7e495e/src/aepp-wallet-communication/schema.ts#L92
*/
const ERROR_CODE_RpcInvalidTransactionError = 2;
/**
* `const ERROR_CODE_RpcBroadcastError = 3;`
*
* See
* https://github.com/aeternity/aepp-sdk-js/blob/1065da9a46b8dbfe60a2c3e5646e7422ee7e495e/src/aepp-wallet-communication/schema.ts#L108
*/
const ERROR_CODE_RpcBroadcastError = 3;
/**
* `const ERROR_CODE_RpcRejectedByUserError = 4;`
*
* See https://github.com/aeternity/aepp-sdk-js/blob/1065da9a46b8dbfe60a2c3e5646e7422ee7e495e/src/aepp-wallet-communication/schema.ts#L122
*/
const ERROR_CODE_RpcRejectedByUserError = 4;
/**
* `const ERROR_CODE_RpcUnsupportedProtocolError = 5;`
*
* See https://github.com/aeternity/aepp-sdk-js/blob/1065da9a46b8dbfe60a2c3e5646e7422ee7e495e/src/aepp-wallet-communication/schema.ts#L140
*/
const ERROR_CODE_RpcUnsupportedProtocolError = 5;
/**
* This error occurs when the user rejects your attempt to connect. (I think)
*
* The error name here is ungrammatical but following the lead of the SDK.
*
* `const ERROR_CODE_RpcConnectionDenyError = 9;`
*
* See https://github.com/aeternity/aepp-sdk-js/blob/1065da9a46b8dbfe60a2c3e5646e7422ee7e495e/src/aepp-wallet-communication/schema.ts#L155
*/
const ERROR_CODE_RpcConnectionDenyError = 9;
/**
* This error occurs when you are not connected to the wallet.
*
* The error name here is ungrammatical but following the lead of the SDK.
*
* `const ERROR_CODE_RpcNotAuthorizeError = 10;`
*
* See https://github.com/aeternity/aepp-sdk-js/blob/1065da9a46b8dbfe60a2c3e5646e7422ee7e495e/src/aepp-wallet-communication/schema.ts#L171
*/
const ERROR_CODE_RpcNotAuthorizeError = 10;
/**
* This error occurs when you are not `address.subscribe`d to the wallet (I think?)
*
* The error name here is ungrammatical but following the lead of the SDK.
*
* `const ERROR_CODE_RpcPermissionDenyError = 11;`
*
* See https://github.com/aeternity/aepp-sdk-js/blob/1065da9a46b8dbfe60a2c3e5646e7422ee7e495e/src/aepp-wallet-communication/schema.ts#L186
*/
const ERROR_CODE_RpcPermissionDenyError = 11;
/**
* This is the general "something went wrong, i dunno" negative error.
*
* See https://github.com/aeternity/aepp-sdk-js/blob/1065da9a46b8dbfe60a2c3e5646e7422ee7e495e/src/aepp-wallet-communication/schema.ts#L196-L209
*/
const ERROR_CODE_RpcInternalError = 12;
/**
* This is presumably the equivalent of the HTTP 404 error
*
* See https://github.com/aeternity/aepp-sdk-js/blob/1065da9a46b8dbfe60a2c3e5646e7422ee7e495e/src/aepp-wallet-communication/schema.ts#L211-L224
*/
const ERROR_CODE_RpcMethodNotFoundError = -32601;
/**
* Error data inside the `error` field of a RpcResp_Err
*/
type RpcError
= {code : number,
message : string,
data? : any};
//-----------------------------------------------------------------------------
// Requests
//-----------------------------------------------------------------------------
/**
* This type is used for "notifications", i.e. messages sent from the client to
* the server that do not need a response. An example is the wallet announcing
* it exists.
*/
type RpcCast
<method_s extends string,
params_t extends object>
= {jsonrpc : "2.0",
method : method_s,
params : params_t};
/**
* This type is used for requests from the client to the server that require a
* response.
*/
type RpcCall
<method_s extends string,
params_t extends object>
= {jsonrpc : "2.0",
id : number | string,
method : method_s,
params : params_t};
//-----------------------------------------------------------------------------
// Responses
//-----------------------------------------------------------------------------
/**
* This is the shape of unsuccessful responses
*/
type RpcResp_error
<method_s extends string>
= {jsonrpc : "2.0",
id : number | string,
method : method_s,
error : RpcError};
/**
* This is the shape of successful responses
*/
type RpcResp_ok
<method_s extends string,
result_t extends any>
= {jsonrpc : "2.0",
id : number | string,
method : method_s,
result : result_t};
/**
* This is the shape of generic responses
*/
type RpcResp
<method_s extends string,
result_t extends any>
= RpcResp_ok<method_s, result_t>
| RpcResp_error<method_s>;
/**
* Most generic possible response
*/
type RpcResp_Any = RpcResp<string, any>;
//=============================================================================
// LAYER 4: VANILLAE-SPECIFIC MESSAGE PROTOCOL
//
// https://github.com/aeternity/aepp-sdk-js/blob/a435e9df5c94004bcd16326b26c38a9c0b284279/src/aepp-wallet-communication/schema.ts#L32-L42
//
// Only the request/responses that I have actually encountered in practice are
// enumerated here. There are many more things that the SDK code appears to
// 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
//----------------------------------------------------------------------------
/**
* Waellet-to-aepp parameters of "connection.announcePresence" cast
*
* (layer 4)
*/
type Params_W2A_connection_announcePresence
= {id : string,
name : string,
origin : string,
type : "window" | "extension"};
/**
* Shape of the waellet-to-aepp "connection.announcePresence" RPC cast
*
* (layer 3)
*/
type RpcCast_W2A_connection_announcePresence
= RpcCast<"connection.announcePresence",
Params_W2A_connection_announcePresence>;
/**
* The actual waellet-to-aepp event passed when the wallet announces it exists
*
* (layer 2)
*
* @example
*
* ```json
* {
* "type": "to_aepp",
* "data": {
* "jsonrpc": "2.0",
* "method": "connection.announcePresence",
* "params": {
* "id": "{aee9e933-52b6-410a-8c3f-99c6be596b4e}",
* "name": "Superhero",
* "networkId": "ae_mainnet",
* "origin": "moz-extension://ee425d81-d5b2-44b6-9406-4da31b019e7c",
* "type": "extension"
* }
* }
* }
* ```
*/
type EventData_W2A_connection_announcePresence
= EventData_W2A<RpcCast_W2A_connection_announcePresence>
//----------------------------------------------------------------------------
// connection.open
//----------------------------------------------------------------------------
/**
* Parameters of aepp-to-waellet "connection.open" call
*
* (layer 4)
*/
type Params_A2W_connection_open
= {name : string,
version : 1,
networkId? : string};
/**
* Result type of "connection.open" call
*
* Same as `Params_W2A_connection_open` empirically
*
* (layer 4)
*/
type Result_W2A_connection_open
= Params_W2A_connection_announcePresence;
/**
* Shape of aepp-to-waellet "connection.open" RPC call
*
* (layer 3)
*/
type RpcCall_A2W_connection_open
= RpcCall<"connection.open",
Params_A2W_connection_open>;
/**
* Shape of waellet-to-aepp "connection.open" RPC response
*
* (layer 3)
*/
type RpcResp_W2A_connection_open
= RpcResp<"connection.open",
Result_W2A_connection_open>;
/**
* The actual aepp-to-waellet "connection.open" event data passed over the message bus
*
* (layer 2)
*/
type EventData_A2W_connection_open
= EventData_A2W<RpcCall_A2W_connection_open>;
/**
* The actual waellet-to-aepp "connection.open" event data passed over the message bus
*
* (layer 2)
*/
type EventData_W2A_connection_open
= EventData_W2A<RpcResp_W2A_connection_open>;
//----------------------------------------------------------------------------
// address.subscribe
//----------------------------------------------------------------------------
/**
* Parameter type of aepp-to-waellet "address.subscribe" call
*
* (layer 4)
*/
type Params_A2W_address_subscribe
= {type : "subscribe",
value : "connected"};
/**
* Result type of waellet-to-aepp "address.subscribe response
*
* (layer 4)
*
* @example
* ```typescript
* {subscription : ["connected"],
* address : {current : {"ak_2Wsa8iAmAm917evwDEZjouvPUXKx2nUv5Uz8e8oNXTDfDXnMRN": {}},
* connected : {}}}
* ```
*/
type Result_W2A_address_subscribe
= {subscription : ["connected"],
address : {current : object,
connected : object}};
/**
* Shape of aepp-to-waellet "address.subscribe" RPC call
*
* (layer 3)
*/
type RpcCall_A2W_address_subscribe
= RpcCall<"address.subscribe",
Params_A2W_address_subscribe>;
/**
* Result of waellet-to-aepp "address.subscribe" response
*
* (layer 3)
*/
type RpcResp_W2A_address_subscribe
= RpcResp<"address.subscribe",
Result_W2A_address_subscribe>;
/**
* Actual aepp-to-waellet "address.subscribe" event data sent over the message bus
*
* (layer 2)
*/
type EventData_A2W_address_subscribe
= EventData_A2W<RpcCall_A2W_address_subscribe>;
/**
* Actual waellet-to-aepp "address.subscribe" event data sent over the message bus
*
* (layer 2)
*/
type EventData_W2A_address_subscribe
= EventData_W2A<RpcResp_W2A_address_subscribe>;
//----------------------------------------------------------------------------
// transaction.sign (do not propagate)
//----------------------------------------------------------------------------
/**
* Parameters for "transaction.sign" (do not propagate)
*
* (layer 4)
*/
type Params_A2W_tx_sign_noprop
= {tx : string,
returnSigned : true,
networkId : string}
/**
* Success result type for "transaction.sign" (do not propagate)
*
* (layer 4)
*/
type Result_W2A_tx_sign_noprop
= {signedTransaction : string};
/**
* Request type for "transaction.sign" (do not propagate)
*
* (layer 3)
*/
type RpcCall_A2W_tx_sign_noprop
= RpcCall<"transaction.sign",
Params_A2W_tx_sign_noprop>;
/**
* Response type for "transaction.sign" (do not propagate)
*
* (layer 3)
*/
type RpcResp_W2A_tx_sign_noprop
= RpcResp<"transaction.sign",
Result_W2A_tx_sign_noprop>;
/**
* Event data for aepp-to-waellet "transaction.sign" (do not propagate) message
*
* (layer 2)
*/
type EventData_A2W_tx_sign_noprop
= EventData_A2W<RpcCall_A2W_tx_sign_noprop>;
/**
* Event data for aepp-to-waellet "transaction.sign" (do not propagate) response
*
* (layer 2)
*/
type EventData_W2A_tx_sign_noprop
= EventData_W2A<RpcResp_W2A_tx_sign_noprop>;
+16
View File
@@ -0,0 +1,16 @@
{"compilerOptions" : {"target" : "es2022",
"strict" : true,
"esModuleInterop" : true,
"skipLibCheck" : true,
"forceConsistentCasingInFileNames" : true,
"noImplicitAny" : true,
"strictNullChecks" : true,
"strictPropertyInitialization" : true,
"sourceMap" : true,
"outDir" : "dist",
"declaration" : true},
"$schema" : "https://json.schemastore.org/tsconfig",
"display" : "Recommended",
"include" : ["src/**/*"],
"exclude" : ["src/jx_include"],
"composite" : true}