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

Implemented Lazy Options #2

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/add-to-devrel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: 'Add to DevRel Project'

on:
issues:
types:
- opened
- reopened
pull_request:
types:
- opened
- reopened

jobs:
add-to-project:
name: Add issue/PR to project
runs-on: ubuntu-latest
steps:
- uses: actions/[email protected]
with:
# add to DevRel Project #117
project-url: https://github.com/orgs/near/projects/117
github-token: ${{ secrets.GH_TOKEN }}
12 changes: 4 additions & 8 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Tests
name: Tests Contract RS
on: push
jobs:
workflows:
Expand All @@ -8,10 +8,6 @@ jobs:
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: "16"
- name: Install modules
run: yarn
- name: Run tests
run: yarn test
- name: Install and test modules
run: |
cargo test
47 changes: 14 additions & 33 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,33 +1,14 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# Developer note: near.gitignore will be renamed to .gitignore upon project creation
# dependencies
node_modules
/.pnp
.pnp.js

# build
/out
/dist
/target
target

# keys
/templates/react/neardev

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
/.cache

npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Rust
**/target
**/Cargo.lock

# TypeScript
**/package-lock.json
**/node_modules/
**/build/
**/yarn.lock
**/.tsimp

# Frontend
**/dist/
**/.parcel-cache
6 changes: 0 additions & 6 deletions .gitpod.yml

This file was deleted.

32 changes: 32 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[package]
name = "contract"
description = "Factory Contract Example"
version = "0.1.0"
edition = "2021"
# TODO: Fill out the repository field to help NEAR ecosystem tools to discover your project.
# NEP-0330 is automatically implemented for all contracts built with https://github.com/near/cargo-near.
# Link to the repository will be available via `contract_source_metadata` view-function.
#repository = "https://github.com/xxx/xxx"

[lib]
crate-type = ["cdylib", "rlib"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
near-sdk = { version = "5.3.0", features = ["unstable"] }

[dev-dependencies]
near-sdk = { version = "5.3.0", features = ["unit-testing"] }
near-workspaces = { version = "0.16.0", features = ["unstable"] }
tokio = { version = "1.12.0", features = ["full"] }
serde_json = "1"

[profile.release]
codegen-units = 1
# Tell `rustc` to optimize for small code size.
opt-level = "z"
lto = true
debug = false
panic = "abort"
# Opt into extra safety checks on arithmetic operations https://stackoverflow.com/a/64136471/249801
overflow-checks = true
197 changes: 181 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,198 @@
# Factory Example
# Factory Contract Example

This example presents a factory of [donation contracts](https://github.com/near-examples/donation-rust). It allows to:
A factory is a smart contract that stores a compiled contract on itself, and
automatizes deploying it into sub-accounts.

1. Create a sub-account of the factory and deploy the stored contract on it (`create_factory_subaccount_and_deploy`).
2. Change the stored contract using the `update_stored_contract` method.
This particular example presents a factory of donation contracts, and enables
to:

<br />
1. Create a sub-account of the factory and deploy the stored contract on it
(create_factory_subaccount_and_deploy).
2. Change the stored contract using the update_stored_contract method.

# Quickstart
```rust
#[payable]
pub fn create_factory_subaccount_and_deploy(
&mut self,
name: String,
beneficiary: AccountId,
public_key: Option<PublicKey>,
) -> Promise {
// Assert the sub-account is valid
let current_account = env::current_account_id().to_string();
let subaccount: AccountId = format!("{name}.{current_account}").parse().unwrap();
assert!(
env::is_valid_account_id(subaccount.as_bytes()),
"Invalid subaccount"
);

Clone this repository locally or [**open it in gitpod**](https://gitpod.io/#/github.com/near-examples/multiple-cross-contract-calls). Then follow these steps:
// Assert enough tokens are attached to create the account and deploy the contract
let attached = env::attached_deposit();

### 1. Install Dependencies
let code = self.code.clone().unwrap();
let contract_bytes = code.len() as u128;
let minimum_needed = NEAR_PER_STORAGE.saturating_mul(contract_bytes);
assert!(
attached >= minimum_needed,
"Attach at least {minimum_needed} yⓃ"
);

let init_args = near_sdk::serde_json::to_vec(&DonationInitArgs { beneficiary }).unwrap();

let mut promise = Promise::new(subaccount.clone())
.create_account()
.transfer(attached)
.deploy_contract(code)
.function_call(
"init".to_owned(),
init_args,
NO_DEPOSIT,
TGAS.saturating_mul(5),
);

// Add full access key is the user passes one
if let Some(pk) = public_key {
promise = promise.add_full_access_key(pk);
}

// Add callback
promise.then(
Self::ext(env::current_account_id()).create_factory_subaccount_and_deploy_callback(
subaccount,
env::predecessor_account_id(),
attached,
),
)
}
```

## How to Build Locally?

Install [`cargo-near`](https://github.com/near/cargo-near) and run:

```bash
cargo near build
```

## How to Test Locally?

```bash
cargo test
```

## How to Deploy?

Deployment is automated with GitHub Actions CI/CD pipeline. To deploy manually,
install [`cargo-near`](https://github.com/near/cargo-near) and run:

```bash
cargo near deploy <account-id>
```

## How to Interact?

_In this example we will be using [NEAR CLI](https://github.com/near/near-cli)
to intract with the NEAR blockchain and the smart contract_

_If you want full control over of your interactions we recommend using the
[near-cli-rs](https://near.cli.rs)._

### Deploy the Stored Contract Into a Sub-Account

`create_factory_subaccount_and_deploy` will create a sub-account of the factory
and deploy the stored contract on it.

```bash
near call <factory-account> create_factory_subaccount_and_deploy '{ "name": "sub", "beneficiary": "<account-to-be-beneficiary>"}' --deposit 1.24 --accountId <account-id> --gas 300000000000000
```

This will create the `sub.<factory-account>`, which will have a `donation`
contract deployed on it:

```bash
npm install
near view sub.<factory-account> get_beneficiary
# expected response is: <account-to-be-beneficiary>
```

### 2. Test the Contract
### Update the Stored Contract

`update_stored_contract` enables to change the compiled contract that the
factory stores.

Deploy your contract in a sandbox and simulate interactions from users.
The method is interesting because it has no declared parameters, and yet it
takes an input: the new contract to store as a stream of bytes.

To use it, we need to transform the contract we want to store into its `base64`
representation, and pass the result as input to the method:

```bash
npm test
# Use near-cli to update stored contract
export BYTES=`cat ./src/to/new-contract/contract.wasm | base64`
near call <factory-account> update_stored_contract "$BYTES" --base64 --accountId <factory-account> --gas 30000000000000
```

---
> This works because the arguments of a call can be either a `JSON` object or a
> `String Buffer`

## Factories - Explanations & Limitations

Factories are an interesting concept, here we further explain some of their
implementation aspects, as well as their limitations.

<br>

### Automatically Creating Accounts

NEAR accounts can only create sub-accounts of themselves, therefore, the
`factory` can only create and deploy contracts on its own sub-accounts.

This means that the factory:

1. **Can** create `sub.factory.testnet` and deploy a contract on it.
2. **Cannot** create sub-accounts of the `predecessor`.
3. **Can** create new accounts (e.g. `account.testnet`), but **cannot** deploy
contracts on them.

It is important to remember that, while `factory.testnet` can create
`sub.factory.testnet`, it has no control over it after its creation.

### The Update Method

The `update_stored_contracts` has a very short implementation:

```rust
#[private]
pub fn update_stored_contract(&mut self) {
self.code.set(env::input());
}
```

On first sight it looks like the method takes no input parameters, but we can
see that its only line of code reads from `env::input()`. What is happening here
is that `update_stored_contract` **bypasses** the step of **deserializing the
input**.

You could implement `update_stored_contract(&mut self, new_code: Vec<u8>)`,
which takes the compiled code to store as a `Vec<u8>`, but that would trigger
the contract to:

1. Deserialize the `new_code` variable from the input.
2. Sanitize it, making sure it is correctly built.

When dealing with big streams of input data (as is the compiled `wasm` file to
be stored), this process of deserializing/checking the input ends up **consuming
the whole GAS** for the transaction.

# Learn More
## Useful Links

1. Learn more about the contract through its [README](./contract/README.md).
2. Check [**our documentation**](https://docs.near.org/develop/welcome).
- [cargo-near](https://github.com/near/cargo-near) - NEAR smart contract
development toolkit for Rust
- [near CLI-rs](https://near.cli.rs) - Iteract with NEAR blockchain from command
line
- [NEAR Rust SDK Documentation](https://docs.near.org/sdk/rust/introduction)
- [NEAR Documentation](https://docs.near.org)
- [NEAR StackOverflow](https://stackoverflow.com/questions/tagged/nearprotocol)
- [NEAR Discord](https://near.chat)
- [NEAR Telegram Developers Community Group](https://t.me/neardev)
- NEAR DevHub: [Telegram](https://t.me/neardevhub),
[Twitter](https://twitter.com/neardevhub)
2 changes: 0 additions & 2 deletions contract/.cargo/config

This file was deleted.

Loading