Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance #[near_bindgen] to be able to bind to trait implementations - would make composition easy into the contract #303

Open
oysterpack opened this issue Mar 2, 2021 · 1 comment

Comments

@oysterpack
Copy link

oysterpack commented Mar 2, 2021

The goal is to be able to write reusable components that can be used to compose a contract. For example, FT (NEP-141), Account Storage (NEP-145), etc. The rusty way of doing this is to leverage auto-traits, i.e., blanket trait implementations.

    1. Component
      trait KeyValueStore { fn get(&self, key: U128) -> Option<U128>; fn set(&mut self, key: U128, value: U128); }

trait HasKeyValueStore

{ fn get_key_value_store(&self) -> &dyn KeyValueStore; fn get_mut_key_value_store(&mut self) -> &mut dyn KeyValueStore; }

impl KeyValueStore for T
where
T: HasKeyValueStore,
{
fn get(&self, key: U128) -> Option

{ self.get_key_value_store().get(key) }

fn set(&mut self, key: U128, value: U128)

{ self.get_mut_key_value_store().set(key, value); }

}

type Data = Object<u128, u128>;

#[derive(Default)]
struct KeyValueStoreService;

impl KeyValueStore for KeyValueStoreService {
fn get(&self, key: U128) -> Option

{ Data::load(&key.0) .unwrap() .map(|object| (*object.value()).into()) }

fn set(&mut self, key: U128, value: U128)

{ Data::new(key.0, value.0).save() }

}


*   the above code provides a `KeyValueStore` implementation for any type thate implements `HasKeyValueStore`
*   `KeyValueStoreService` is the resuable component that implements the `KeyValueStore`

1.  1.  Contract
        ```rust
        #<span class="error">[near_bindgen]</span>
        #<span class="error">[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)]</span>
        pub struct Contract { #[borsh_skip] key_value_store: KeyValueStoreService, }

type Data = Object<u128, u128>;

#<span class="error">[near_bindgen]</span>
impl Contract {
#<span class="error">[init]</span>
pub fn init() -> Self {
assert!(!env::state_exists(), "contract is already initialized");
Self 

{ key_value_store: KeyValueStoreService, } 

}
}

impl HasKeyValueStore for Contract {
fn get_key_value_store(&self) -> &dyn KeyValueStore 

{ &self.key_value_store } 

fn get_mut_key_value_store(&mut self) -> &mut dyn KeyValueStore 

{ &mut self.key_value_store } 

}
  • the contract would be able to implement the KeyValueStore trait by implementing the HasKeyValueStoreby wiring in the KeyValueStoreService
  • the problem is that the KeyValueStore implementation is not bound by near_bindgen

The work around is to implement the trait directly and delegate manually:

#<span class="error">[near_bindgen]</span>
impl KeyValueStore for Contract {
fn get(&self, key: U128) -> Option<U128> 

{ self.key_value_store.get(key) } 

fn set(&mut self, key: U128, value: U128) 

{ self.set(key, value) } 

}

Naturally, you don't want to pull in all auto-traits, the trait implementations to pull in should be explicitly defined. I imagine something like this:

 #<span class="error">[near_bindgen(KeyValueStore, FungibleToken)]</span>
 #<span class="error">[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)]</span>
 pub struct Contract 

 { #[borsh_skip] key_value_store: KeyValueStoreService, } 
  • in the above example, KeyValueStore and FungibleToken trait functions would be bound
  • and to round it out, near_bindgen would need to also support marker annotations on the trait, e.g., something like
 #<span class="error">[near_bindgen_trait]</span>
 trait KeyValueStore 

 { fn get(&self, key: U128) -> Option<U128>; #[payable] fn set(&mut self, key: U128, value: U128); } 
  • in the example above, set is marked as payable
  • near_bindgen_trait would be a new marker attribute
@swfsql
Copy link

swfsql commented Sep 29, 2021

I'm currently exploring ways to achieve what's stated in this issue (for now, prototyping in here).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: NEW❗
Development

No branches or pull requests

2 participants