Awesome
Social DB
Notes
Testnet account ID
Deployed at v1.social08.testnet
https://explorer.testnet.near.org/accounts/v1.social08.testnet
Mainnet account ID
Deployed at social.near
https://explorer.near.org/accounts/social.near
About empty keys
If a leaf value was originally string, e.g. name
{
"alex.near": {
"profile": {
"name": "Alex"
}
}
}
And later some value was added under name
, the name will be transformed to a node, and the value
will be moved to an empty key under this node. E.g. name/foo = "bar"
is added.
{
"alex.near": {
"profile": {
"name": {
"": "Alex",
"foo": "bar"
}
}
}
}
Changelog
See CHANGELOG.md
API
Storing data
The top level keys of the objects should be account IDs under which the data is stored. Those accounts are covering storage for the underlying data.
The predecessor_id or the signer public key should have permission to write under those keys. If the predecessor_id matches the top level key, then it can write any data under that key, as long as it has a permission or at least 1 yoctoNEAR is attached.
The attached deposit will be transferred to the first key. If the account doesn't exist, it will be created (the predecessor_id should match).
#[payable]
pub fn set(&mut self, data: Value);
Arguments:
data
is an object to store. The leaf values should be strings or null values. String values will be added, while null values will be deleted.
Examples:
set({
data: {
"alex.near": {
"profile": {
"name": "Alex",
"image": {
"url": "https://gkfjklgdfjkldfg"
}
},
}
}
})
set({
data: {
"alex.near": {
"graph": {
"follow": {
"root.near": "",
"bob.near": "",
}
}
}
}
})
Reading data
Returns the data for a list of given key patterns. It takes one or more path patterns as arguments, and returns the matching data. The path pattern is a string that can contain wildcards. For example:
alice.near/profile/**
will match the entire profile data of accountalice.near
.alice.near/profile/*
will match all the fields of the profile, but not the nested objects.alice.near/profile/name
will match only the name field of the profile.*/widget/*
will match all the widgets of all the accounts.
pub struct GetOptions {
pub with_block_height: Option<bool>,
pub with_node_id: Option<bool>,
pub return_deleted: Option<bool>,
}
pub fn get(self, keys: Vec<String>, options: Option<GetOptions>) -> Value;
Arguments:
keys
- an array of key patterns to return.options
- optional argument to specify options.
Options:
with_block_height
- if true, for every value and a node will add the block height of the data with the key:block
.with_node_id
- if true, for every node will add the node index with the key:node
.return_deleted
- if true, will include deleted keys with the valuenull
.
Returns the aggregated JSON object.
Examples:
get({keys: ["alex.near/profile/name"]})
get({keys: ["alex.near/profile/name", "root.near/profile/name"]})
get({keys: ["alex.near/profile/name", "alex.near/profile/description"]})
get({keys: ["alex.near/profile/tags/*"]})
get({keys: ["alex.near/profile/**"]})
get({keys: ["*/widget/*"]})
get({keys: ["alex.near/profile/tags/*"], options: {return_deleted: true}})
Reading keys
The keys
method allows to get the list of keys that match the path pattern.
It's useful for querying the data without reading values.
It also has an additional options
field that can be used to specify the return type and whether to return deleted keys.
For example:
alice.near/profile/*
will return the list of all the fields of the profile, but not the nested objects.*/profile/image/nft
will return the list of all the accounts that have an NFT image in their profile.alice.near/widget/*
withreturn_deleted
option will return the list of all the widget names of the account, including the deleted ones.alice.near/widget/*
withreturn_type
equal toBlockHeight
will return the list of all the widget names of the account and the value will be the block height when the widget was last updated.- Note
**
is not supported by thekeys
method.
pub enum KeysReturnType {
True,
BlockHeight,
NodeId,
}
pub struct KeysOptions {
pub return_type: Option<KeysReturnType>,
pub return_deleted: Option<bool>,
pub values_only: Option<bool>,
}
pub fn keys(self, keys: Vec<String>, options: Option<KeysOptions>) -> Value;
Arguments:
keys
- an array of key patterns to return.options
- optional argument to specify options.
Options:
return_type
- ifBlockHeight
, will return the block height of the key instead oftrue
, ifNodeId
, will return the node index of the key instead oftrue
.return_deleted
- if true, will include deleted keys.values_only
- iftrue
, only matches keys which value is not a node. It's needed to filter out deleted entries. Since a node can't be deleted right now.
Returns the aggregated JSON object.
Examples:
keys({keys: ["alex.near/profile/*"]})
keys({keys: ["*/profile/image/nft"]})
keys({keys: ["alex.near/widget/*"], options: {return_deleted: true}})
keys({keys: ["alex.near/widget/*"], options: {return_type: "BlockHeight", values_only: true}})
Permissions
See https://explorer.testnet.near.org/transactions/3c7h9da1z5Px4JumNDsRaJtCDQaZHG46dsc2SnAj5LHx\
#[payable]
pub fn grant_write_permission(
&mut self,
predecessor_id: Option<AccountId>,
public_key: Option<PublicKey>,
keys: Vec<String>,
);
/// Returns true if the permission is granted for a given account ID or a given public_key to
/// any prefix of the key.
pub fn is_write_permission_granted(
&self,
predecessor_id: Option<AccountId>,
public_key: Option<PublicKey>,
key: String,
) -> bool;
Debugging
export CONTRACT_ID=v1.social08.testnet
export ACCOUNT_ID=eugenethedream
# Full contract data
near view $CONTRACT_ID get '{"keys":["**"]}'
# Full account's data
near view $CONTRACT_ID get '{"keys":["'$ACCOUNT_ID'/**"]}'