Coder Social home page Coder Social logo

hackbg / fadroma Goto Github PK

View Code? Open in Web Editor NEW
49.0 6.0 14.0 179.52 MB

Magical flying bulldozer for the Cosmosverse. Contains clients for Tendermint-based chains in various degrees of completeness, and a mini build system for deploying systems of multiple interdependent smart contracts from source.

Home Page: https://fadroma.tech

License: GNU Affero General Public License v3.0

Rust 59.06% JavaScript 6.09% TypeScript 32.72% Nix 0.05% Dockerfile 0.13% Pug 0.59% CSS 1.35% Shell 0.01%
smart-contracts cosmwasm cosmos rust typescript build-system nodejs tendermint

fadroma's Introduction

Fadroma Scriptable User Agents for the Blockchain

Fadroma defines a unified TypeScript API for interacting with blockchains that are based on Tendermint and CosmWasm.

The Fadroma Agent API provides the following features:

To talk with a particular chain, you need to install one or more of the following packages alongside @hackbg/fadroma:

Fadroma is truly free software developed at Hack.bg, available under GNU AGPLv3. Custom licensing is available for paying users.

fadroma's People

Contributors

a-stgeorge avatar aakamenov avatar denismaxim0v avatar dependabot[bot] avatar egasimus avatar floppydisck avatar hoomp3 avatar ivaylonikolov7 avatar mradkov avatar omahs avatar wiz1991 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

fadroma's Issues

Fadroma 21.07

  • Need to converge main branch with 21.06.1 branch first.
  • Let's give every component its own semantic version and use semantic-release to enforce it based on commit messages.
  • Calendar versions for framework releases and semantic versions for individual components would hopefully provide a satisfactory resolution to all the versioning shenanigans.

Ensemble: option to auto-increment block height/time

When testing contracts with logic relies on block height or time we need to manually keep a variable and increment it between calls. This gets tedious and error prone. An optional configuration in the Context struct could be implemented that takes care of that logic.
It should be possible to configure the block height and time separately and allow them to be increased by a set amount or a random value between a set interval for each call.

Don't build builder image more than once

If the builder image is missing and multiple contracts are built in parallel, each build starts building its own copy of the build container image. If this fails halfway the user is left with a bunch of untagged Docker images taking up space

Derive macro: Return types on interface queries are not enforced.

If you have an interface like:

#[interface]
pub trait MyInterface {
  #[query("my_query")]
  fn my_query() -> StdResult<Foo>;
}

in the implementation, you can do:

#[contract_impl(path = "path_to_interface")l]
pub trait MyInterfaceImpl {
  #[query("my_query")]
  fn my_query() -> StdResult<Bar> {
    ...
  }
}

and it will compile. The macro should enforce that the implementation returns StdResult<Foo> instead of StdResult<Bar>.

make `contract!` macro more flexible

  1. Currently, anything used by the contract root module must be pub in order to be accessible from the internally generated msg, query and handle submodules (which are hidden from the contract author)

    • this is confusing because it deviates from Rust convention around pub
    • one option that must be explored is to return local closures from the dispatch matchers instead of generating modules with handler functions
  2. Currently, the internal prelude! macro uses a fixed set of imports across the root contract module as well as the generated submodules (used for dispatching query/response handlers).

    • this is confusing because the generated handler submodules are hidden from the contract author (as they were never meant to be used directly, only via passing the corresponding msg).
    • and can break compilation if the contract author uses some of the same definitions manually outside the contract macro
    • the contract! macro should wrap the entire module, uses, types and all, changing:
#[macro_use] extern crate fadroma;
type Foo = Uint128;
contract!(...)

into:

#[macro_use] extern crate fadroma;
contract!(
    type Foo = Uint128;
    ...
)
  1. Currently, the contract! macro expects [State], [Init], [Query], [Response] and [Handle] sections in that exact order (even if any of them is empty).
    • Having contract! operate on a generic list of items/tts would hopefully open a way to use #[meta] annotations to denote "magic", allowing the sections from 1. to look more like regular Rust.
    • However, too much flexibility might need the macro to become procedural; let's avoid that if possible

@fadroma/ops/Core: chain of custody

client.instance.template.artifact.source should point to the original Source object from which the contract was built (if this info is available)

Avoid the use of `serde_json::Value` in `DeserializeFlat`

The purpose of the DeserializeFlat derive macro is to avoid the nested JSON representation of enum tuple variants. This is really important when we want integrate with external services/apps like the Keplr wallet that expect an exact message format. We can't simply not utilize tuple variants as this is how reusable contract components are implemented in the derive-contract macro.

A practical example is trying to create a SecretNetwork viewing key via Keplr. Keplr expects the contract to accept the following message: { set_viewing_key: { key: "my_key", padding: null } }, which would require the handle message enum in our contract to have a variant that looks like this:

#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum HandleMsg {
    SetViewingKey {
        key: String,
        padding: Option<String>,
    }
}

We can avoid implementing this logic in our contract by using Fadroma's auth module and including it in the contract definition. However, the resulting message generated by the derive-contract macro will look something like this:

#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum HandleMsg {
    Auth(AuthHandleMsg)
}

making the JSON that the contract actually accepts to be this: { auth: { set_viewing_key: { key: "my_key", padding: null } } }.

DeserializeFlat provides a custom implementation of the serde::de::Deserialize trait that tries to deserialize the incoming JSON as the standard derive(Deserialize) would first and if that fails, it tries to deserialize each of the tuple enum variants directly (in the above example that would the AuthHandleMsg type).

The current implementation of this macro makes use of serde_json's Value type which defeats the entire purpose of the serde_json_wasm crate to not use dynamic memory allocation so this is not really usable in its current state. It also doesn't actually work, since calls to deserialize_any are forbidden in serde_json_wasm and this method is called by the serde_json::Value type.

How can the current logic be implemented without using serde_json or ideally, not having knowledge of what Deserializer implementation is actually being used. Alternative ideas are very welcome as well!

Epic: Documentation

  • output of cargo doc
  • output of tsdoc
  • rendered versions of .spec.ts.md files
  • lcov report
  • Website: link each feature section to appropriate source, API docs, test spec, coverage report, and dev guides
  • Guide: project setup
  • Guide: contract features
  • Guide: deployment scripts

imply `$tx_state`

  • $tx_state is local to generated handler functions. Moving the ok! macro definition into the handler function template allows ok!(...) to be used instead of ok!(state,...).
  • Currently, the form ok!(_,...) means "success, no state updates necessary". This feature is not used by anyone yet, and if comparing 2 $State objects is cheaper than writing the same $State object, then this enhancement would benefit adding such a comparison instead of this to avoid saving unchanged $State objects.

@fadroma/gui: Auto-generate contracts demo based on schema

Generate a static page/egui page based on the generated contract schema and the WASM blobs from the compiled code, which will allow the developer to click-trough the whole contract functionality in a web page.

Can be integrated with Keplr so that they interact with an account of theirs, even when running on a localnet.

add local bindings to `$*_msg` attributes

That would change:

contract!(
    [Init] (deps, env, msg: { foo: Foo }) {
        State { foo: msg.foo }
    }

into:

contract!(
    [Init] (deps, env, msg: { foo: Foo }) {
        State { foo } // `msg.foo` is still available
    }

fadroma-procmacros: `#[message]` macro

/// before:
#[derive(Clone,Debug,PartialEq,Serialize,Deserialize,JsonSchema)]
#[serde(rename_all="snake_case")]
#[serde(deny_unknown_fields)]
pub enum Response {
    TokenInfo((String, String)),
    Balance(Uint128)
}

/// after:
#[message] Response {
    TokenInfo((String, String)),
    Balance(Uint128)
}

Optional feature: deduplicated variant constructors:

/// with variant constructors (optional):
#[message] Response {
    TokenInfo((String, String)): fn token_info (core) {
        Ok(Self::TokenInfo(("Hello".into(), Self::helper())))
    }
    Balance(Uint128): fn balance (core, address: HumanAddr, key: String) {
        Ok(Self::Balance(Uint128::MAX))
    }
    /// a variant is also optional in this position:
    fn helper () -> String { "World".into() }
}

// Usage:
let (hello, world) = Response::token_info(core)?

Tidy up the roadmap

Lets break down everything in tangible issues and update the ROADMAP.md

  • 100% test coverage.

  • Docker Compose integration for a portable development environment based on familiar tools.

  • Fadroma CLI. Command-line entrypoint for creating new projects and running default and custom management commands.

    • (init) Start a project
    • (add) Add contracts to it
    • (compile) Compile them
  • Fadroma GUI.

    • Visualize dependencies between contracts.
    • Automatically generate microfrontends from smart contract schema, to let you easily construct management dashboards and product UIs.
    • Generate and browse documentation of your project and its dependencies.
    • Explore blockchain activity.
  • Advanced testing tools for your smart contracts:

    • WASM-FFI integration for testing production-ready smart contract builds.
    • BDD support to allow non-programmers to specify test cases using a Cucumber-like language and reusable test steps generated from the contract schema.
    • Fuzzing, property-based testing, eventually formal validation.
    • View test results and coverage reports in a friendly Web UI.

`struct Field`

Making this the tracking issue for struct Field, an atomic storage field that knows its own address and memoizes its value.

Pseudo-example:

#[contract]
struct Contract {
  admin: Field<Addr>
}

impl Contract {
  fn new (storage: Storage) -> Self {
    Self { admin: Field::new(storage, KEY_ADMIN_ADDR) }
  }
  fn assert_admin (&self, env: Env) -> StdResult<()> {
    if self.admin.get() != env.message.sender {
      return Err(StdError::unauthorized())
    }
    Ok(())
  }
}

I'd like to avoid having a .get() method, so Field should implement Deref/DerefMut

Epic: Vanilla CosmWasm, IBC, Portability

If we enable support for vanilla CosmWasm (maybe in a separate branch or different version), we might expand the support for contracts for other cosmos networks as well.


EDIT by @egasimus:

  • Reorganize crates and packages` to make room
  • Add fadroma-platform-cw, @fadroma/cw, etc
  • Identify what's the same and what's different across versions
  • Provide version-specific implementations of what differs
  • Provide a unified version-independent interface

implement `#[derive(Humanize, Canonize)]`

Implementations of humanize/canonize a.k.a. to_normal/to_stored are repetitive and work in the same way:

  1. replace all humanizable/canonizable members of a struct with their humanized/canonized versions
  2. copy any other members verbatim.

Let's use our newly developed macro capabilities to get rid of that repetition!

  • ๐Ÿ‘€ Observation: addresses are converted between their two representations on entering/leaving storage.

    • Implication: maybe this can be integrated more fully with the storage mechanism, so that the API consumer never sees the dichotomy?
    • Implication: more importantly, is it possible to change the trait method from fn (&self,... to fn (self,... and consume the struct when converting to the opposite format? this sounds like it would save some copy/clone operations and be more gas-efficient
  • ๐Ÿš€ Starting points:

    1. Macro example https://github.com/hackbg/fadroma/blob/main/scrt-contract/lib.rs#L17 (this one also derives the serde and schemars traits - maybe it's easiest to just add a #[address] marker to that?)
    2. Procedural macro example https://github.com/hackbg/fadroma/blob/main/scrt-admin/require-admin/src/lib.rs
    3. Read up on how expansion works - maybe something like Foo { ..self, bar: bar.humanize( would suffice, no macros needed (although the signatures would remain tedious to write)

SGX-less integration testing in GitHub actions

Here I've implemented some bindings that allow a compiled WASM contract to be executed in a pure JS environment. Basic example here.

The objective is to integrate that with Mocha and scrt-agent, so that we can more easily write and run JS-based integration tests - including in GitHub Actions without all the headaches associated with external runners.

Feel free to do it on the nice-bindings branch (which will probably become fadroma-21.08 in the future). Bonus points for a GitHub action that runs the tests. Mega bonus points for figuring out a way to export Rust coverage (via some sort of Tarpaulin integration?) from that and publish it to Coveralls from the action.

Assigning @htunlogic because he wrote the new scrt-agent Wrapper.js - still haven't had the time to wrap my head around that code :)

Research SGX emulation

We find ourselves in the need of simulating the SGX environment (at least for implementing automated test runners).

Agent class should be able to use keys added via `secretcli add`

Option 1. Use something similar to https://github.com/zealic/go2node to get private keys from the system keyring by the same means as secretcli.

This looks like it would necessitate a custom Go binary based on secretcli to be built for different platforms.

Option 2. The alternative would be to talk to the supported keyrings directly from JS:

const (
        BackendFile    = "file"
        BackendOS      = "os"
        BackendKWallet = "kwallet"
        BackendPass    = "pass"
        BackendTest    = "test"
        BackendMemory  = "memory"
)

Add example project

could be something simple, like a counter, calculator, or name->address mapping

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.