10 months ago, we submitted the Treasury Proposal #0 for Redspot v0.1 and described the development plan for the next 4 versions. A few weeks ago, we submitted the Treasury Proposal #44 for Redspot v0.4, and now we have completed all the plans. Redspot has become a powerful development environment, testing framework and asset pipeline for Pallet-Contracts. Redspot is trying to let the development of ink! be projectized and simplify the testing and interacting with contracts. The source code and docs are at https://github.com/patractlabs/redspot/
The core functions of Redspot have been completed in v0.3. In Redspot v0.4, we mainly made some optimizations on the user experience, such as adding docker support, adding a GUI interface, supporting browser extension signatures, and other optimizations. Now let us show the design and implementation of v0.4, as well as how to run and verify it. Like v0.3, you can install a template project locally via npx redspot-new erc20
. For specific usage methods, please refer to previous reports or our documents.
The official compilation tool of ink! is cargo-contract and it does not support the compilation of multi-contracts. You need to write additional scripts to fulfill. But in Redspot v0.4, we have implemented multi-contracts compilation very well. It supports automatic detection of contract files and simultaneous compilation of contracts in multiple languages.
For the ink! contract, Redspot will find all cargo.toml files in the configured directory, and then parse the cargo.toml file. If it contains ink_lang dependencies, it will consider this to be an ink! contract. Then you can find all the contracts under the project.
For solang contracts, Redspot can find all contracts by matching .sol files.
The compilation configuration of Redspot's default template contract is as follows:
...
export default {
...
contract: {
ink: {
toolchain: "nightly",
sources: ["contracts/**/*"],
},
},
...
};
Among them, toolchain
represents the toolchain that needs to be used when compiling the contract, and sources
represents the directory that needs to be found, and supports glob syntax. At the same time, the sources
option can also be specified through the command line, which will override the configuration options in the config file:
npx redspot compile "contracts/**/*"
You can also specify a single directory, this can compile a single contract:
npx redspot compile "contracts/erc20"
Solang is also a similar configuration:
...
export default {
...
contract: {
ink: {
toolchain: "nightly",
sources: ["contracts/**/*"],
},
solang: {
sources: ['contracts/**/*.sol']
}
},
...
};
Ink! and solang can exist at the same time. Redspot uses cargo-contract for ink! contract and solang compiler to compile solang contract. We will also provide support for other compilers in the future.
All compilation products can be found under artifacts (or a directory configured by the user):
-- artifacts
-- erc20.contract
-- erc20.json
redspot will generate a .contract
file and a .json
file for the same contract. .contract
contains all the information of .json
, and additionally contains the source code of wasm. So .json
is only provided to the front-end and used when the front-end does not need to upload wasm.
Note that the names of multiple contracts in the project should not be the same. redspot will detect this situation and report an error. An example of contract compilation can be found.
Clone the Redspot repository and enter the redspot/examples/multi-contract
directory. Then install dependencies:
$ yarn
Then you need to make sure you have installed cargo-contract and solang. redspot will not install them automatically.
Then run the compilation command:
$ npx redspot compile
This will compile all contracts at once, including solang contracts, which can be found under contracts/filpper. The compiled product can be found in the artifacts
directory, and then we delete the artifacts
folder. Run the command:
$ npx redspot compile contracts/erc20
Can compile erc20 contracts. Check the artifacts
folder, only the compiled product of the erc20 contract.
redspot known types
is a plugin that automatically adds types of known chains.
In polkadot, adding types is very troublesome, because they must be added manually instead of automatically generated. And sometimes it is necessary to deal with the compatibility issues of different chain versions.
We hope that users of Redspot can focus on contract development and not care about these details. So we added this plugin, the purpose is to solve the problem of types of different chains.
Most chains have submitted types on Polkadot Apps, and all types are defined in the npm package @polkadot/apps-config
. So we will partially quote the types definition in @polkadot/apps-config
. But @polkadot/apps-config
lacks the types definition of the development environment. Then we will make up this part manually. Currently supported chains are Canvas, Jupiter, Europa, Edgeware, Plasm and Clover.
We have added this plugin to the template by default, and users do not need to perform additional configuration. If you need to add this plugin manually, first install the dependencies:
$ yarn add @redspot/known-types
Then in the redspot.config
file, import the plugin:
...import "@redspot/known-types"...export default { ...};
We can verify through the Jupiter (you can use the online testnet of wss://ws.jupiter-poa.patract.cn/ ) chain. We change the node of the configuration file to connect to Jupiter, and then take care to delete all types:
...
// import "@redspot/known-types"
...
export default {
...
networks: {
development: {
endpoint: "wss://ws.jupiter-poa.patract.cn/",
},
types: {},
...
}
...
};
Then start redspot console:
$ npx redspot console
Then run commands to query the authorities information. Because jupiter's unique type AuthorityState is needed here, and we have not defined the AuthorityState type, so there will be a parsing error:
$ await network.api.isReady;(await network.api.query.poA.authorities("3eTmoLUQtK2dsVFtzLUztfPSd3KghZggMpnz1XdzVt33vK9c")).toHuman()
RPC-CORE: getStorage(key: StorageKey, at?: BlockHash): StorageData:: Unable to decode storage poA.authorities:: createType(AuthorityState):: Cannot construct unknown type AuthorityState
Uncaught:
Error: Unable to decode storage poA.authorities:: createType(AuthorityState):: Cannot construct unknown type AuthorityState
Then we can try to introduce @redspot/known-types to fix this problem
...
import "@redspot/known-types"
...
export default {
...
networks: {
development: {
endpoint: "wss://ws.jupiter-poa.patract.cn/",
},
types: {},
...
}
...
};
Then follow the same steps as before. At this time, because @redspot/known-types will identify the connected chain and inject known type definitions, including the AuthorityState type, it can now be successfully parsed:
$ npx redspot console
> await network.api.isReady;(await network.api.query.poA.authorities("3eTmoLUQtK2dsVFtzLUztfPSd3KghZggMpnz1XdzVt33vK9c")).toHuman()
'Working'
```
In Redspot v0.4, we added support for using docker to compile contracts. This will ensure that the wasm compiled under different platforms is consistent.
Redspot will use redspot/contract this docker image to compile and run the testnet. It is modified on the basis of contract docker image provided by Parity Tech.
Currently only supports ink! contract docker compilation, the configuration is as follows:
...
export default {
...
contract: {
ink: {
toolchain: "nightly",
docker: true,
sources: ["contracts/**/*"],
},
},
...
docker: {
sudo: false,
},
...
};
Before running the compile command, please make sure that docker is installed on this machine. Run command
$ npx redspot compile
⚠️Attention
When using docker to compile, it may be affected by the network environment (such as the need for a vpn proxy in China Mainland), which will cause the compilation time to be long.
If you use ctrl+c to exit the current compilation command in the middle, the docker container will not automatically stop deleting.
Now it will compile with docker by default. If you want to change the default behavior, please add the --docker false
parameter:
$ npx redspot compile --docker false
Note that if you encounter permission errors, please change the docker.sudo
in the redspot.config
file to true. This will use sudo to run docker.
Similarly, you can also use docker to run a testnet. The canvas testnet is currently built in. In the future, we will build more testnets for users to choose from.
The current command to run the testnet is:
$ npx redspot testnet
In fact, its role is just to run the command:
$ docker run -p 9944:9944 --rm redspot/contract /bin/bash -c "canvas --tmp --dev --ws-port=9944 --ws-external"
If you want to modify the default running command, you can add the command parameter:
$ npx redspot testnet --command 'docker run -p 9945:9944 --rm redspot/contract /bin/bash -c "canvas --tmp --dev --ws-port=9944 --ws-external"'
We will recommend and guide users to give priority to using docker, because this can focus on the writing of contracts and avoid a lot of trouble in environment configuration and node startup.
We found that most users use @polkadot/apps or Canvas UI to assist in debugging when developing contracts. So we developed redspot explorer based on @polkadot/apps, with the purpose of replacing @polkadot/apps in contract development. For this we have done a lot of modifications and optimizations. And the native code and web page are connected in a certain program. We mainly made these optimizations:
The network configured in redspot config will be injected into explorer.
The account configured in the redspot config will appear in the explorer and can be used in the explorer. For example, you can perform transactions such as transfers, just like the web account.
The types configured in redspot will be adopted by explorer.
The .contract file of the contract generated in the local project will be automatically uploaded to the explorer. The contracts page in the explorer will list all the contracts known to the code.
When switching networks, only the contract and abi and account numbers of the current network are displayed.
You can use polkadot{.js} extension to sign when you run tests or scripts locally.
Able to send instructions to compile code locally in explorer.
You can run redspot scripts in explorer.
If there is a known code, the contract event and the parameters when sending the contract will be automatically parsed in the block details.
You can inquire about transactions related to the contract.
To use redspot explorer, you need to install the @redspot/explorer
plugin:
$ yarn add @redspot/explorer
Then add below in redspot.config:
...
import "@redspot/explorer";
...
Run redspot explorer:
npx redspot explorer
Note that when running redspot explorer, a service will be started locally, please do not terminate the process. And, please restart the service manually every time when redspot.config is changed.
Then open the default URL http://127.0.0.1:8011
Open switch network
As you can see, you can select the network configured in redspot.
Check the accounts page, the account configured in redspot.config will appear.
Then you can use the account in your polkadot extension to make transactions. Copy your account address in polkadot extension.
Then change the deploy.ts script:
import { network, patract } from "redspot";
const { getContractFactory } = patract;
const { api } = network;
async function run() {
await api.isReady;
const signer = "<your address>";
const contractFactory = await getContractFactory("erc20", signer);
const balance = await api.query.system.account(signer);
console.log("Balance: ", balance.toHuman());
const contract = await contractFactory.deployed("new", "1000000", {
gasLimit: "200000000000",
value: "100000000000",
});
await contract.tx.transfer(signer, 7);
console.log("");
console.log(
"Deploy successfully. The contract address: ",
contract.address.toString()
);
api.disconnect();
}
run().catch((err) => {
console.log(err);
});
Then run the script $ npx redspot run ./scripts/deploy.ts
, and then call the polkadot extension for signing. Check the polkadot extension in your browser (note that the explorer page must be open)
Then deploy the contract. Then after the deployment is successful, check the explorer page:
There will be events on the chain. We look at the contracts.instantiateWithCode event,
There will be an analysis of contract message and contract event.
Check the codes page of contracts:
The code of the locally compiled contract will automatically appear here.
Then check the contracts page of contracts:
The contract just deployed will appear here.
Then we switch to the console page, where we can run the script and compile the contract:
We click compile contract and this prompt appears to indicate that the compilation was successful:
If the compilation time is too long, please switch to the terminal running the npx redspot explorer
command and check the log prompt.
Then we click run to run the script, and the console on the right will output the run log:
Now that the contract is successful, let's switch to the contracts page to check:
As you can see, the newly deployed contract will appear here.
When we click on the extrinsics button (the blue "extrinsics" in each row in the "recent contracts" in the figure), a dialog box will pop up, showing the relevant transactions of the current contract:
@redspot/decimals
is a plug-in that automatically converts the precision of numbers, such as converting 1 DOT
to 10000000000
.
Currently, the functions implemented by this plug-in are as follows.
Fixed decimal
1 DOT
=> 10 ** 10
1 KSM
=> 10 ** 12
Get the tokenDecimal
of the chain automatically
1 UNIT
=> 10 ** tokenDecimal
Install:
yarn add @redspot/decimals
And add the following statement to your redspot.config.ts
:
import '@redspot/decimals'
Use when deploying the contract:
const contract = await contractFactory.deployed('new', '10000', {
gasLimit: '400000000000',
value: '10000 UNIT'
});
Use in transfer
import { network } from 'redspot';
const { api } = network;
async function run() {
await api.isReady;
api.tx.balances.transfer(address, '1 UNIT')
}
This is because that @polkadot/api
implements its own set of type system. Therefore, we have rewritten Weight
and Balance
to recognize functions similar to 1 DOT
. If the function used is not the type of Weight
and Balance
, the above method will not take effect.