Skip to content

Commit

Permalink
update contracts and readme
Browse files Browse the repository at this point in the history
  • Loading branch information
agryaznov committed Jan 4, 2024
1 parent d79508e commit f4e2624
Show file tree
Hide file tree
Showing 5 changed files with 1,454 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
.idea
*.log
tmp/

bin/
target
.projectile
67 changes: 55 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ This project is an **experimental** add-on to Polkadot SDK's [pallet-contracts](
## Quickstart

Start the Polkamask development node
Start the Polkamask development node:

```bash

cargo run -- --dev
```

Expand All @@ -43,33 +42,77 @@ Also import add the *well-known* development accounts:
> [!CAUTION]
> It is **highly recommended** to use a separate MetaMask instance for this (e.g. in a dedicated <a href="https://support.mozilla.org/en-US/kb/profiles-where-firefox-stores-user-data" target="_blank">browser profile</a>), not to mix the development accounts (whose private keys **are compromised** by design) with your real money-holding accounts.
**That's it!** You should right away be able to communicate with the Duck 🦆 chain using your MetaMask. Let's try it as described in the following section.
**That's it!** You should right away be able to communicate with the Duck 🦆 chain using your MetaMask. Let's see it in action, as described in the following section.

## Demo 🧐

Our little demo consists of the three basic actions we complete on our *Substrate*-based 🦆 network using *MetaMask*:
Our little demo consists of the three basic actions we complete on our *Substrate*-based 🦆-network using *MetaMask*:

1. **👛 Transfer tokens**
with the MetaMask UI controls solely.
1. **👛 Transfer tokens**.
With the MetaMask UI controls solely.

This is the simplest action as we already have everything set up to do this.
Once launched the Polkamask node with `cargo run -- --dev`, just open your MetaMask and make sure it is connected to our 🦆 network. You should see *Alith* account holding `10000000 🥚`. Go ahead and send some amount of eggs to *Goliath* or any other account you'd like to (set gas limit to `21000` as requested by MetaMask).

2. **⚡ dApp (simple): tokens transfer**
via *web3js*-based dApp used with MetaMask for signing transactions.
2. **⚡ dApp (simple): tokens transfer**.
Via *web3js*-based dApp used with MetaMask for signing transactions.

3. **🚀 dApp (advanced): ink! + MetaMask**
call *ink!* smart contract via *web3js*-based dApp using *MetaMask* for signing transactions.
3. **🚀 dApp (advanced): ink! + MetaMask**.
Call *ink!* smart contract via *web3js*-based dApp using *MetaMask* for signing transactions.

For the actions 2,3 we have a simple [dApp](/dapp) which consists of a static [web page](/dapp/index.html) and of course our [**ink!** contract](/dapp/contracts/flipper.ink/). This needs a bit of preparatory job to be done to set things up first.


### Prepare

Our 🦆-chain has _pallet-contracts_ and at the same time works with _Ethereum_ 20-bytes _Account_ format. The latter fact is required so that our node can understand MetaMask-signed transactions. But for the existing polkadot contracts tooling this is an unusual setting, as they're expected to work with 32-bytes long _Accounts_.

For this reason, to work with our **ink!** contracts on this chain, we use a fork of _cargo-contract_ tool which speaks with our node the same language! Run this command to install it:

``` bash
cargo install --git https://github.com/agryaznov/cargo-contract --branch polkamask --force
```

### Set Up

`TBD`
#### Build contract

**ink! contract**

```bash
cd dapp/contracts/flipper.ink
cargo contract build
```

**Solidity contract (optional)**

> [!NOTE]
> In order to get Ethereum _web3js_ lib work with our contract, we **gotta make it believe it deals with an Ethereum contract**.
> For that, we need to generate a metadata for our contract in the proper format. For the purposes of our PoC demo, we uploaded a ready-to use JSON file for that. Though if you'd like you can install <a href="https://trufflesuite.com/" target="_blank">truffle</a> tool and make it yourself as described below.
>
> **Keep in mind** that in the future this step is intended to be done by existing ink! tooling (e.g. _cargo-contract_).

``` bash
cd dapp/contracts/flipper.ink
truffle build
```

1. Build contract
2. Deploy contract

Make sure you've started the polkamask-node:

```bash
cargo run -- --dev
```

Then deploy the contract:

```bash
cd dapp/contracts/flipper.ink
cargo contract deploy
```

3. Contract address to dApp code.

### Run
Expand Down
115 changes: 115 additions & 0 deletions dapp/contracts/flipper.ink/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#![cfg_attr(not(feature = "std"), no_std, no_main)]

#[ink::contract]
pub mod flipper {
#[ink(storage)]
pub struct Flipper {
value: bool,
}

impl Flipper {
/// Creates a new flipper smart contract initialized with the given value.
#[ink(constructor)]
pub fn new(init_value: bool) -> Self {
Self { value: init_value }
}

/// Creates a new flipper smart contract initialized to `false`.
#[ink(constructor)]
pub fn new_default() -> Self {
Self::new(Default::default())
}

/// Flips the current value of the Flipper's boolean.
#[ink(message, selector = 0xcde4efa9)]
pub fn flip(&mut self) {
self.value = !self.value;
}

/// Returns the current value of the Flipper's boolean.
#[ink(message)]
pub fn get(&self) -> bool {
self.value
}
}

#[cfg(test)]
mod tests {
use super::*;

#[ink::test]
fn default_works() {
let flipper = Flipper::new_default();
assert!(!flipper.get());
}

#[ink::test]
fn it_works() {
let mut flipper = Flipper::new(false);
assert!(!flipper.get());
flipper.flip();
assert!(flipper.get());
}
}

#[cfg(all(test, feature = "e2e-tests"))]
mod e2e_tests {
use super::*;
use ink_e2e::build_message;

type E2EResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;

#[ink_e2e::test]
async fn it_works(mut client: ink_e2e::Client<C, E>) -> E2EResult<()> {
// given
let constructor = FlipperRef::new(false);
let contract_acc_id = client
.instantiate("flipper", &ink_e2e::alice(), constructor, 0, None)
.await
.expect("instantiate failed")
.account_id;

let get = build_message::<FlipperRef>(contract_acc_id.clone())
.call(|flipper| flipper.get());
let get_res = client.call_dry_run(&ink_e2e::bob(), &get, 0, None).await;
assert!(matches!(get_res.return_value(), false));

// when
let flip = build_message::<FlipperRef>(contract_acc_id.clone())
.call(|flipper| flipper.flip());
let _flip_res = client
.call(&ink_e2e::bob(), flip, 0, None)
.await
.expect("flip failed");

// then
let get = build_message::<FlipperRef>(contract_acc_id.clone())
.call(|flipper| flipper.get());
let get_res = client.call_dry_run(&ink_e2e::bob(), &get, 0, None).await;
assert!(matches!(get_res.return_value(), true));

Ok(())
}

#[ink_e2e::test]
async fn default_works(mut client: ink_e2e::Client<C, E>) -> E2EResult<()> {
// given
let constructor = FlipperRef::new_default();

// when
let contract_acc_id = client
.instantiate("flipper", &ink_e2e::bob(), constructor, 0, None)
.await
.expect("instantiate failed")
.account_id;

// then
let get = build_message::<FlipperRef>(contract_acc_id.clone())
.call(|flipper| flipper.get());
let get_res = client.call_dry_run(&ink_e2e::bob(), &get, 0, None).await;
assert!(matches!(get_res.return_value(), false));

Ok(())
}
}
}
2 changes: 0 additions & 2 deletions dapp/contracts/flipper.sol/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,3 @@
.idea
*.log
tmp/

build
Loading

0 comments on commit f4e2624

Please sign in to comment.