Skip to content
This repository has been archived by the owner on Sep 28, 2022. It is now read-only.

tE3m/SmartConnections

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

38 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SmartConnections

This project is an application for this Gitcoin bounty. The connections are stored on-chain using a Smartweave smart contract. The contract is live on the arweave with the ID 8zsrYKY_ZD9MJZcYjjpq4rajA2WGPAWrrq6IdfL4GnM, the source has the TxID 3yLz25dBkgp-y1O2F6dCpUT-U5ctSw4oj29rFPIzxLI. A basic frontend that reads out the connections and sorts them by most recent can be found here. More info on that app on this branch.

Philosophy and approach

This smart contract is very user-centered in the way the methods work. Connections can only be altered by their owners, the address that called the contract for this specific interaction. Even though there is a list of addresses with privileged write access in owners, the only special access those have is to extend the already present namespaces and connection types. This is there to control the addition of new namespaces and connection types. As a protection mechanism, the owners cannot be changed as to not introduce multiple levels of privilege. All of this means that there's unrestricted read access to this smart contract's data, however, extremely limited write access which guarantees the safety and permanence (until requested removal) of the stored data. Due to the underlying technology, each address pays for its own connections. This price is very low (currently, and also expected to be so in the future), however, it isn't zero, which hopefully makes users more mindful of their digital footprint.

Why Smartweave

Smartweave allows storing the data on the Arweave network. The way it works makes extending the contracts very easy for two reasons:

  1. The contract's source is stored on-chain as well, which means that any Smartweave contract is inherently open-source, even if its development isn't (unlike this project)
  2. Any contract that's deployed on the chain already can also be called as the source for a new contract with a new initial state
    • This mechanic also means that in the unlikely event of a breaking bug the contract's source can be adapted, re-deployed and continue working with the old contract's state

You can find more information on the way Smartweave evaluates contracts and how you can interact with them on the project's github page or in these blog entries: [1] [2]

Setup

To initiate a new instance of this smart contract, follow these steps:

  1. Install Smartweave (if not already installed):

    npm install -g smartweave
  2. Write an initial state.json:

    {
      "owners": ["owner1", "owner2"],
      "namespaces": {
        "namespace1": ["method1", "method2"],
        "namespace2": ["method1", "method2"]
      },
      "connections": {}
    }
  3. Create a new contract:

    smartweave 3yLz25dBkgp-y1O2F6dCpUT-U5ctSw4oj29rFPIzxLI path/to/state.json --key-file path/to/keyfile

Usage

Simplified explanations of the different methods of interaction with the contract's data Smartweave provides:

  1. readContract basically just returns the contract's state at the current (or passed) height. This is useful, because unlike the other methods, this one doesn't require passing a wallet since it simply reads out the current state without any further computation.
  2. interactRead also reads the contract, but this method requires passing a wallet and input, since it computes the output based on these parameters. This is very useful however, if we don't want the contract's state in the format it is saved in on the chain, which will be further elaborated on later.
  3. interactWrite is the most interesting one, if you will, since this method allows writing the (connection) data to be written to the contract's state. This is also the only method of the three that requires a private wallet, since this is the only one that actually costs Ar to use.

readContract

This smart contract's state's format is as follows:

type State = {
    owners: string[],
    namespaces: {
        [namespace: string]: string[]
    },
    connections: {
        [origin: string]: {
            [target: string]: {
                [namespace: string]: {
                    [connectionType: string]: {
                        createdAt: number,
                        alias: string | null
                    }
                }
            }
        }
    }
}

owners: An array of addresses with access to configuration methods (unchangable once deployed)

namespaces: An object containing the namespaces and their respective connection types

connections: nested objects, keyed by each parameter a connection can have. This format makes it easier to process the write calls, but it might not be the preferred format when actually working with the data. That's why, alternatively to the basic readContract() option of retrieving the data, there are two more:

interactRead

followings

Calling the contract with the input {function: "followings"} will return all followings an address has as an array, keyed by the following address.

Input:

input = {
    function: "followings",
    target?: string,
    namespace?: string
}

target: Only this address' followings are returned (optional)

namespace: Only followings in this namespace are returned (optional)

Output:

output = {
    [origin: string]: [{
        connectionType: string,
        target: string,
        namespace: string,
        createdAt: Number,
        alias: string | null
        },
    ]
}

followers

Calling the contract with the input {function: "followers"} will return all followers an address has as an array, keyed by the address that is being followed.

Input:

input = {
    function: "followers",
    target?: string,
    namespace?: string
}

target: Only this address' followings are returned (optional)

namespace: Only followings in this namespace are returned (optional)

Output:

output = {
    [followed: string]: [{
        connectionType: string,
        origin: string,
        namespace: string,
        createdAt: Number,
        alias: string | null
        },
    ]
}

interactWrite

Important notice: Writing data to the Arweave chain like these methods do costs Ar. The price is calculated from the input data's size and can be obtained from https://arweave.net/price/{input_data_size}

follow

This method allows the calling address to follow a target address in a specific namespace with a specific connection type.

Input:

input = {
    function: "follow",
    target: string,
    namespace: string,
    connectionType: string,
    alias?: string
}

target: The address to connect to

namespace: The namespace the connection is in

connectionType: The connection type of this connection

alias: An alias for this connection (optional)

unfollow

This method allows the calling address to delete previously established followings. If no parameters are passed, delete all followings.

Input:

input = {
    function: "unfollow",
        target?: string,
        namespace?: string,
        connectionType?: string
}

target: The address to disconnected from (optional)

namespace: The namespace the connection is in (optional)

connectionType: The connection type of the connection (optional)

alias: An alias for this connection (optional)

addNamespaces

This method allows addresses that are members of the owners group to add namespaces to this contract's state.

Input:

input = {
    function: "addNamespaces",
    namespaces: {
        [namespace: string]: string[]
    }
}

namespaces: An object containing the namespaces as keys and their respective connection types as values

addConnectionTypes

This method allows addresses that are members of the owners group to add connection types to an already present namespace.

Input:

input = {
    function: "addConnectionTypes",
    namespace: string,
    connectionTypes: string[]
}

namespace: The namespace to add the connection types to

connectionTypes: An array of connection types