This project is the delivery of the Open Grant #268 - Quadratic Funding Module and Dapp Application and built on top of Substrate Framework.
Quadratic Funding is a common short name for Constrained Liberal Radicalism algorithm (referred to as CLR in this proposal) proposed by Vitalik Buterin 2018, and is a crowdfund matching mechanism for public goods, like open source. The authors of the paper summaries the mathematical idea as below.
"Individuals make public goods contributions to projects of value to them. The amount received by the project is (proportional to) the square of the sum of the square roots of contributions received."
The mechanism is a concrete proposal for making grassroots donations something much larger by proportionately matching more towards small donations compared to large ones. It has been efficiently funding projects in the Web3 ecosystem.
A user flow and simple demonstration of the application is shown using below UML sequence diagram.
User flow diagram on Lucid Chart
This section explains two way of running this project, with Rust environment and with Docker
Follow the Rust setup instructions before using the included Makefile to build the Node Template.
This project uses a Makefile to document helpful commands and make it easier to execute them. Get started by running these make
targets:
make init
- Run the init script to configure the Rust toolchain for WebAssembly compilation.make run
- Build and launch this project in development mode.
The init script and Makefile both specify the version of the Rust nightly compiler that this project depends on.
The make run
command will perform an initial build. Use the following command to build the node without launching it:
make build
The make run
command will launch a temporary node and its state will be discarded after you terminate the process. After the project has been built, there are other ways to launch the node.
First, install Docker and Docker Compose.
Then run the following command to start a single node development chain.
./scripts/docker_run.sh
This command will firstly compile your code, and then start a local development network. You can
also replace the default command (cargo build --release && ./target/release/node-template --dev --ws-external
)
by appending your own. A few useful ones are as follow.
# Run Substrate node without re-compiling
./scripts/docker_run.sh ./target/release/node-template --dev --ws-external
# Purge the local dev chain
./scripts/docker_run.sh ./target/release/node-template purge-chain --dev
# Check whether the code is compilable
./scripts/docker_run.sh cargo check
Functions in this pallet can be tested using single-node development chain mode, but if necessary developers can also set up multi-chain network as instructed below.
This command will start the single-node development chain with persistent state:
./target/release/node-template --dev
Purge the development chain's state:
./target/release/node-template purge-chain --dev
Start the development chain with detailed logging:
RUST_LOG=debug RUST_BACKTRACE=1 ./target/release/node-template -lruntime=debug --dev
If you want to see the multi-node consensus algorithm in action, refer to our Start a Private Network tutorial.
Please refer to Quadratic Funding Pallet documentations for development.
End-to-end testing using Polkadot.js library to simulate developer experience from web application is used to test against code in this repo. All test code are in quadratic-funding-test repo. There are two types of test cases, unit tests and functional tests.
Unit testing is a method to test individual units of code separately. The purpose is to separate the smallest testable parts, in this case a Rust function, and verify if they function properly in isolation.
Unit tests are categorized as below.
Scenario | Number |
---|---|
approve | 5 |
cancel | 5 |
cancelRound | 3 |
contribute | 5 |
createProject | 3 |
finalizeRound | 2 |
fund | 2 |
scheduleRound | 10 |
withdraw | 5 |
In functional testing, a tester is to verify the output based on the user requirements with the expected output. The main objective of Functional Testing is to check the entire pallet and the networking interface – a simulation of an end-to-end development experience.
Functional tests are categorized as below.
Scenario | Number |
---|---|
approve | 7 |
cancel | 7 |
cancelRound | 3 |
contribute | 4 |
finalizeRound | 3 |
scheduleRound | 2 |
withdraw | 7 |
Account | Identity |
---|---|
ALICE | Root |
Bob | Project Owner |
DAVE | Contributor |
-
Open the Polkadot.js Apps (https://polkadot.js.org/apps/#/) and connect to our pallet.
-
Add Additional Types to Polkadot.js Apps Settings.
Click
Settings
->Developer
, and copy the below json content, than save and reload.{ "ProjectIndex": "u32", "ProjectOf": "Project", "RoundIndex": "u32", "RoundOf": "Round", "Round": { "start": "BlockNumber", "end": "BlockNumber", "matching_fund": "Balance", "grants": "Vec<Grant>", "is_canceled": "bool", "is_finalized": "bool" }, "Grant": { "project_index": "ProjectIndex", "contributions": "Vec<Contribution>", "is_approved": "bool", "is_canceled": "bool", "is_withdrawn": "bool", "withdrawal_expiration": "BlockNumber", "matching_fund": "Balance" }, "Contribution": { "account_id": "AccountId", "value": "Balance" }, "Project": { "name": "Vec<u8>", "logo": "Vec<u8>", "description": "Vec<u8>", "website": "Vec<u8>", "owner": "AccountId" } }
-
Transfer some values to
DAVE
.Click
Accounts
, selectALICE
, and send 10000 units toDAVE
. -
Contribute 1000 units to fund pool.
Click
Developer
->Extrinsics
, select theALICE
account,openGrant
pallet andfund
method, input 1000 units, then submit.
-
Success test case with
contribute
functional-
Create a new project by using
BOB
account.Click
Developer
->Extrinsics
, select theBOB
account,openGrant
pallet andcreateProject
method, input the project info, then submit.If success, you can get the project info by
Develop
->Chain State
, selectopenGrant
pallet andprojects
method, inputProjectIndex
with0
. -
Schedule a new round by using
ALICE
account.Click
Developer
->Extrinsics
, select theALICE
account,sudo
pallet andsudo
method, selectopenGrant.scheduleRound
call, input the new round info (start
should large than currrent block, andend
should large thanstart
), then submit.If success, you can get the round info by
Develop
->Chain State
, selectopenGrant
pallet androunds
method, inputRoundIndex
with0
. -
When the new round is active (current block > start and current block < end), using DAVE account to contribute 100 units to BOB's project (project_index is 0).
If success, you can get the contribute info by
Develop
->Chain State
, selectopenGrant
pallet androunds
method, inputRoundIndex
with0
.
-
-
Fail test case with
withdraw
functional when the project owner (BOB
) wants to withdraw a not approved project.-
After the active round is ended (
end
> current block), finalize the ended round by usingALICE
account.Click
Developer
->Extrinsics
, select theALICE
account,sudo
pallet andsudo
method, selectopenGrant.finalizeRound
call, input the endedRoundIndex
with0
, then submit.If success, you can get the finalized round info by
Develop
->Chain State
, selectopenGrant
pallet androunds
method, inputRoundIndex
with0
. You can find theis_finalized
istrue
now. -
Using
BOB
account to withdraw the project's fund.Click
Developer
->Extrinsics
, select theBOB
account,openGrant
pallet andwithdraw
method, input theRoundIndex
with0
andProjectIndex
with0
, then submit. After a while you can get an error, because if the project owner (BOB
) wants to withdraw, he first needs the root account (Alice
) appove his project.
-
-
Success test case with
withdraw
functional-
Approve
BOB
's project by usingALICE
account.Click
Developer
->Extrinsics
, select theALICE
account,sudo
pallet andsudo
method, selectopenGrant.approve
call, input the finalizedRoundIndex
with0
and need approvedProjectIndex
with0
, then submit.If success, you can get the contribute info by
Develop
->Chain State
, selectopenGrant
pallet androunds
method, inputRoundIndex
with0
. You can find theis_approved
istrue
inBOB
's project now. -
Using
BOB
account to withdraw the project's fund.Click
Developer
->Extrinsics
, select theBOB
account,openGrant
pallet andwithdraw
method, input theRoundIndex
with0
andProjectIndex
with0
, then submit.If success, you can get the contribute info by
Develop
->Chain State
, selectopenGrant
pallet androunds
method, inputRoundIndex
with0
. You can find theis_withdrawn
istrue
inBOB
's project now and can get other informations.
-