perun-network / erdstall-ts-sdk Goto Github PK
View Code? Open in Web Editor NEWTypeScript client SDK to interact with Erdstall.
License: Apache License 2.0
TypeScript client SDK to interact with Erdstall.
License: Apache License 2.0
Field sig
of SignedTradeOffer
should be a required property (is optional in TradeOffer
).
If possible, creation of a copy should be avoided.
This can be used to improve the type safety of trade offer handling in NERD's MarketplaceServer
.
subscribe()
call to new backend implementation:
subscribe("balanceProof", filterArgs)
subscribe("txs")
BalanceProof
s have to be adapted to new backend implementation, see erdstall-operator
.subscribe()
to public Erdstall
interface:
TxReceipt
s.TxReceipts
in a Result
call.LedgerConnection
and EnclaveConnection
:
EnclaveConnection
interface into erdstall.ts
subscribe()
interface method into some interface, this might be EnclaveWatcher
or a new Subscriber
.index.ts
, to ease importing from the SDK.In #18, a package anchor #erdstall
in the form of an internal imports
specification in package.json
was created. The corresponding paths
translation was also added to tsconfig.json
and all files in src/test
were updated to already use this new form of import.
I propose that we change all (complex) imports, that is, not short ones like import * from './ledger'
in immediately surrounding files/dirs to use the package anchor.
We could also add a second #test
import spec to package.json:imports
that points to src/test
and also then finally remove the test->src/test
symlink. Of course, we could also just use #erdstall/test
but since it's just an internal reference, we can spare a second one :)
To complete this issue, also remove the dev dependency tsconfig-paths
and its mocha registration in the test
script and from the tsconfig.*.json
files.
subscribe
currently subscribes to both, receipts and balance proofs. It should only subscribe to what handlers are set for. For the time being, we don't require it to be smart enough to add subscriptions if any handler is set after a subscribe
call. It is sufficient that, if a user adds a new handler, they have to call subscribe
again. This should also be documented in the API.
It still uses npm
. Can also enable the caching
option of the node setup action.
We found out that the standard format for addresses in JSONs use all-lower-case 0x
prefixed strings, not the checksum strings. So this has to be changed when marshalling addresses, in particular maps that use addresses as keys, like Values
.
As part of this issue, it should be made sure that Values
keys are always in lower-case format.
This also has to be changed in the Erdstall operator (https://github.com/perun-network/erdstall-ext/issues/96).
As discussed in #89 (comment)
Looking at this and the fact we have es2020
as a target
set in our tsconfig.json
, we expect no problems.
However, including our lib in front end applications throws an error, in which bigint ** bigint
falsely throws an Uncaught TypeError: Cannot convert a BigInt value to a number
.
Exponentiation like this is clearly supported as can be seen here, so we either have a problem with our project setup or this is a bug.
Further investigation needed. For the time being, we can just replace 2n ** 256n - 1n
with the following:
const prettyNumber = 1n << 256n;
export const MAX_AMOUNT_VALUE = prettyNumber - 1n;
Using a temporary variable because prettier
does not allow to use ( )
here to ensure correct operator precedence...
We currently have abominations like the following in our API:
async leave(): Promise<Stages<Promise<ethers.ContractTransaction>>> {
It would be very convenient to:
This would result in the following:
async leave(): AsyncIterator<SomeType> {
or similar.
For this we could probably use the default interface of an AsyncIterator
and extend it slightly with an optional length?: number
value. it.length
would then, if not undefined
, allow a developer to display a proper progressbar in a frontend application, while the fact that an Iterator
is returned perfectly conveys the fact, that multiple transactions are being await
ed.
The intricacies of the implementation, whether the Stages
helper is really necessary or just has to implement the AsyncIterator
interface is up for the implementer to research and decide.
...and update existing API description.
Currently this is required:
const tx = await session.mint(nftAddress, nftId);
const minttx = tx.tx as Mint;
I think it would be nice to have a polymorphic TxReceipt<T extends Transaction>
helper, which types the internal transaction:
const minttx: TxReceipt<Mint> = await session.mint(nftAddress, nftId);
This can then be set in the return values of our interfaces and should be straight forward to implement.
The most relevant API surface should be properly documented using TSDoc, which in turn leads to properly generated JSDoc in the output JS files.
Current implementation has a big problem ๐คก
// NewBigInt generates a random `BigInt` in range 0 <= `CEIL`.
export function NewBigInt(rng: PRNG, CEIL: bigint): bigint {
const base = CEIL / 2n;
const offset = (BigInt(Math.floor(100 * rng.uFloat32())) * base) / 100n; // <-- Not good.
const add = (v1: bigint, v2: bigint): bigint => {
return v1 + v2;
};
const sub = (v1: bigint, v2: bigint): bigint => {
return v1 - v2;
};
const op = rng.uFloat32() < 0.5 ? add : sub;
return op(base, offset);
}
This results in a lot of repeated values and false negatives in some tests. Even better if we can find a battletested library which we could utilise for stuff like this.
If an individual asset's withdrawal throws during a withdraw
on-chain call, the Erdstall
smart contract doesn't revert the whole transaction, but just throws a WithdrawalException
for this particular asset. This is a safety mechanism to avoid a single token from reverting other token withdrawals indefinitely.
This event still needs to be exposed on the Erdstall client API.
When testing this, the RevertToken
from can be used to trigger a revert during a withdrawal.
Track whether a connection is already fully initialized or disconnected and throw an error that details this condition. Currently, it throws a DOMException: An attempt was made to use an object that is not, or is no longer, usable
, which is completely useless for debugging.
And once this is setup, we can use the published package in dependent projects' CIs, e.g. in https://github.com/perun-network/nerd-marketplace/
It would be great to add a reference to a minimal working example.
Originally posted by @matthiasgeihs in #11 (comment)
Currently our PRNG
allows us to replay our tests because it logs the seed, but we are now at a point, where filtering the seed out of the testlog is rather cumbersome.
We should extend the test.newPrng()
to log the testsuite it is part of. mocha
gives some abilities to inspect the running test and maybe there is even a fail/error callback we could use to add our testseed?
Whatever route we take, this has to be changed.
As discussed in the mentioned thread.
Originally posted by @sebastianst in #82 (comment)
Also related to issue #79
Currently, the end-to-end-test does not run in the CI. Would be nice to have it, especially because it would've caught a bug after the implementation of typed-handlers.
We should add means to the client to query nft metadata. OpenSea has docs on metadata standards.
The interfaces could look like
interface NFTMetadata {
name: string;
description: string;
image: string; // image URL
attributes?: []Attribute;
background_color?: string; // six-char hex
// ... more optional fields?
}
interface Attribute {
// please complete with OpenSea metadata standards fields
}
interface NFTMetadataProvider {
getNftMetadata(token: Address, id: bigint): Promise<NFTMetadata>;
}
For generic NFTs that exist on-chain, the implementation should
tokenURI
on-chain (and cache it for future requests)In the meantime, we should adapt the backend server to conform to this standard (https://github.com/perun-network/nerd-marketplace/issues/100) so we can use the generic implementation also for our PerunArt example contract.
The frontend should then use this functionality from the SDK to display any NFT.
The ErdstallClient
and ErdstallSession
require a provider to be able to communicate with the underlying ledger. From a user perspective it would be nice to have the Provider
exposed.
Example:
I am currently a user of this SDK and heavily use the ErdstallSession
in my code. For 9 out of 10 things it does the job I need and gives a way to access whatever I need. But the tenth thing is connecting to a Contract
on the ledger. To make this work, I have to use a Provider and it would be nice to just be able to retrieve that Provider form the Session
I use everywhere else. Otherwise I am forced to manually track this provider, which is possible but not feels awkward, when it is right in front of me but locked behind a protected
directive.
Any thoughts?
Emit when receiving balance proofs.
Use in Client.leave()
.
In file ledger/backend/contracts_deposit.spec.ts
currently only ETH and ERC20 deposits are tested.
We want to query on-chain registered contracts for different token types with the client. I propose the following function signature on the interface LedgerReader
.
interface TokenRegistration {
address: Address, // token contract address
type: string // token type "ETH" | "ERC20" | "ERC721"
holder: Address // token holder address, where deposits can be made to
symbol?: string // optional token symbol, if contract supports symbol() call
}
LedgerReader.getRegisteredTokens(filter?: { tokenType?: string, tokenSymbol?:string}): TokenRegistration[]
Note that token contracts not necessarily support the symbol() string
call, only if they implement ERC20Detailed.
A default implementation should be added to the Client
. The call would query all on-chain registered tokens by filtering to the TokenRegistered
events. If the optional filter is given, it would filter to only those registrations that match the specified tokenType
or tokenSymbol
.
The prepack
script should copy the README file into the dist
folder, from which the npm package is built.
The TxReceipt subscription should hold the same information as what's returned by tee.Enclave.ProcessTX
, that is, all changed accounts in a map.
This should be done after https://github.com/perun-network/erdstall-ext/issues/71
When a client, who is using our SDK, would try to go low level and implement some custom EnclaveProvider
TypedJSON
seems not to be initialised correctly.
Calls like TypedJSON.parse(<my-event-json>, sdk.api.result.Result)
do not return the expected Result
type indicating that TypedJSON
does not call our defined en-/decoding handlers.
However, using the Enclave
itself works fine.
We can register our custom JSON serialization of bigint
directly with TypedJSON's type mapping. ABI handling can probably also be set per type.
Will be introduced in https://github.com/perun-network/erdstall-ext/issues/84
To aid the intuition of developers using our SDK we should strive to use a familiar API for our types.
{Asset|Assets}
includes a cmp(...)
method, while types like Address
and BigInteger
implement an equals
method.
I propose to add the equals
method where possible. Currently that would be in {Asset|Assets}
.
See also here.
The SDK mixes Go conventions with JS/TS conventions (like Google's) for the naming of variables/classes/functions/....
E.g. many exported functions start with a capital letter like test.NewRandom...
.
For some reason, depositing still only works with fungibles, it seems. At least I got that error when testing my deposit dialog.
As mentioned in #77 (comment), we mixed up the semantics of test setup and tests cases.
The way it is right now works, so this issue has low priority, but it would be nice to find common grounds regarding structuring of tests for future reference.
Relies on #21.
Currently TypeTags
is defined as an interface with the keys Amount
and Tokens
.
The corresponding Asset.typeTag()
function returns a simple string
. It would be beneficial to constrain the return type of Asset.typeTag()
to be a sum type of the possible typeTag
s, currently "uint" | "idset"
.
This sum type should be compiler derived from the TypeTags
type, which limits the number of changes necessary until the compiler can throw all necessary errors to make refactoring a simple task of following these errors and extend implementation. In the future all we would have to do is extend the TypeTags
type and the rest gets derived on its own from tsc
.
This would allow users to exhaustively switch over the Asset.typeTag()
function enabling more rigorous implementations and gives confidence when updating the SDK:
const resolveAssetAmount = (a: Asset): TypeTagsValue => {
switch (asset.typeTag()) {
case TypeTags.Amount:
return utils.formatEther((a as Amount).value);
case TypeTags.Tokens:
return (a as Tokens).value.length.toString();
// no default case. User gets compile time error here when an
// updated SDK version has extended the `TypeTags` type.
}
};
Users are forced to use a default
case currently because asset.typeTag()
returns a string
. Any update to the SDK will not lead to an error here and just fails at runtime, which is inconvenient.
As mentioned in #69 (comment), we are not really sure how to properly expose errors thrown by our SDK.
IMO, it would be ideal if we could allow users to do the following:
try {
// some awesome SDK functions/methods used.
} catch (e) {
switch (sdk.asErdstallError(e)) {
case "Connection": // Handle connection error.
case "Signature": // Handle signature error.
}
// Standard way to handle errors.
}
I realise this is opinionated, but I really dislike the way errors have to be checked in "default" TS. Finding a trusted and battletested library which could be used to take some burden off of the user and guiding them to properly handle exceptions/errors is beneficial in the long run.
Cache event handlers before execution of either phase shift or balance proof events to break order dependency of event invocation, since both types of event come from the same cause.
A phase shift handler currently only receives a signal without any information. We could pass the old epoch number and block number to the handler as an
interface PhaseShiftEvent {
epoch: number; // just passed epoch
blockNumber: number;
}
The LedgerConnection
, EnclaveConnection
and Erdstall
interfaces currently mix up read-only operations (state reads and event subscriptions) and write operations (sending of transactions). This is problematic because a read-only user like a marketplace only needs the read parts. Also, the implementations need signers, which a read-only actor doesn't have and shouldn't need to provide.
So we split up the interfaces and call the read-only part Client
/Reader
and the extended write part Session
/Writer
.
Interface Erdstall
from erdstall.ts
should become ErdstallSession
. It extends ErdstallClient
, which only extends the read-only interfaces. The Enclave
and Ledger
connections are split into Reader
and Writer
parts.
The following tree shows how interfaces extend (embed) each other, and mentions some methods explicitly.
ErdstallSession
ErdstallClient
LedgerReader
ErdstallWatcher
(I'd prefer to rename to LedgerWatcher
so that prefix Erdstall
stands for combined stuff. Same for LedgerEvent
etc.)erdstall(): Address
EnclaveReader
EnclaveWatcher
getAccount
LedgerWriter
Depositor
Withdrawer
EnclaveWriter
Onboarder
(new interface, contains method onboard
)Transactor
Minter
Exiter
Leaver
I'd also add an interface Connector
that has the two methods connect
and disconnect
and extend it in interfaces that need it.
I'd move method subscribe
into the watcher
interface because on, once, off
go together with subscribe
, if I understand it correctly.
The implementations also need to be split up, so that we can have a read-only Client
that implements the ErdstallClient
interface and a writing, user-bound ErdstallSession
that implements the Session
interface.
Since we use 1. #erdstall
via an imports
config in package.json
and 2. a paths
translation in tsconfig
, we should be able to remove the package tsconfig-paths
and also all registrations, e.g. in the mocha test scripts (-r tsconfig-paths/register
). Note that we also translate paths in the output transpilate using ttsc
.
I was able to remove it in nerd-marketplace
as well.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.