Typechain-polkadot
Package for generating TypeScript definitions & runtime code for Polkadot smart contracts.
Usage
In your project install this package:
npm i -D @727-ventures/typechain-polkadot
Now you can use it to generate TS definitions & runtime code for Polkadot smart contracts. To use typechain-polkadot.
Note, that ink! contracts generate two files:
metadata.json
and<contract-name>.contract
. You need to provide both of them to typechain-polkadot, and renamemetadata.json
to<contract-name>.json
.
Typechain can be used in two ways:
- As a CLI tool
- As a library
CLI tool
After installing the package, you can use it as a CLI tool. To use it, run the following command:
npx @727-ventures/typechain-polkadot --input path/to/abis --output path/to/output
Methods and namespaces used in the typechain, and their description
build-extrinsic
In this namespace you can find all the functions that are related to building extrinsics.
const tx = contract.buildExtrinsic.<methodName>(...args, options);
tx.signAndSend(account, (result) => {
// Handle result
});
constructors
Used to deploy contracts, using different constructors.
Let's deploy the following contract:
#![cfg_attr(not(feature = "std"), no_std)]
#![feature(min_specialization)]
#[openbrush::contract]
pub mod my_psp22 {
// imports from openbrush
use openbrush::contracts::psp22::*;
use openbrush::traits::Storage;
#[ink(storage)]
#[derive(Default, Storage)]
pub struct Contract {
#[storage_field]
psp22: psp22::Data,
}
// Section contains default implementation without any modifications
impl PSP22 for Contract {}
impl Contract {
#[ink(constructor)]
pub fn new(initial_supply: Balance) -> Self {
let mut _instance = Self::default();
_instance._mint_to(_instance.env().caller(), initial_supply).expect("Should mint");
_instance
}
}
}
This contract has a constructor new
with one argument initial_supply
.
To deploy this contract, you need to use the following code:
// Import here Constructors and Contract classes
// Here we are creating an instance of the Constructors class, which is used to deploy contracts,
// Constructors is typechain-generated class that contains all the constructors of the contract
const factory = new Constructors(api, UserAlice);
// You can access to the different constructors using the name of the constructor, here we will use "new"
const {result, address} = await factory.new('10', {});
// Here we are creating an instance of the Contract class, which is used to interact with the deployed contract
contract = new Contract(address, UserAlice, api);
contract
Contract is the main namespace for interacting with contracts. It contains all the functions that are related to contracts.
const contract = new Contract(
address,
signer,
nativeAPI,
)
contract.name() // get the name of the contract
contract.address() // get the address of the contract
contract.abi() // get the abi of the contract
contract.<namespace>.<functionName>(...args, options) // call a function from a namespace
// namespace can be tx, query, events, etc.
contract.withSigner(signer)
// change the signer of the contract in the current context,
// basically it will create a new contract with the new signer
contract.withAddress(address)
// change the address of the contract in the current context,
// basically it will create a new contract with the new address
// useful for proxy contracts
contract.withAPI(api)
// change the api of the contract in the current context
// basically it will create a new contract with the new api
data
Utility file. Contains all info about types. It's used in runtime to parse return values from contracts.
mixed-methods
This namespace contains both tx and query methods.
contract.mixedMethods.<functionName>(...args, options)
query
This namepsace contains all query methods
const result = contract.query.<functionName>(...args, options)
console.log(result.value)
You can also use it to get errors from contracts
try {
await contract.withSigner(UserBob).query.transfer(UserAlice.address, '10', []);
} catch ({ _err }) {
console.log(_err);
}
console.log
{ insufficientBalance: null }
tx-sign-and-send
This namespace is used send transactions.
const result = await contract.tx.<functionName>(...args, options)
Library
You can also use typechain-polkadot as a library. To use it, you need to import it in your code:
import {Typechain} from '@727-ventures/typechain-polkadot/src/types/typechain';
import {testPathPatternToRegExp} from "jest-util";
const typechain = new Typechain();
typechain.loadDefaultPlugins();
typechain.run(
pathToInput,
pathToOutput
)
Plugins
Typechain-polkadot uses plugins to generate code. By default, it uses the following plugins:
- build-extrinsic docs
- constructors docs
- contract docs
- data docs
- events docs
- events-types docs
- mixed-methods docs
- query docs
- tx-sign-and-send docs
You can also create your own plugins to add some custom logic to the typechain-polkadot. To do this, you need to create a class that implements the TypechainPlugin
interface:
import {TypechainPlugin} from '@727-ventures/typechain-polkadot/src/types/interfaces';
import {Abi} from "@polkadot/api-contract";
/**
* generates a contract file
*
* @param abi - The ABI of the contract
* @param fileName - The name of the file to write to
* @param absPathToOutput - The absolute path to the output directory
* @param absPathToABIs - The absolute path to the ABIs directory
*/
function generate(abi: Abi, fileName: string, absPathToOutput: string, absPathToABIs: string) {
console.log('Hello World!');
}
export default class HelloWorldPlugin implements TypechainPlugin {
name: string = 'HelloWorld';
outputDir: string = 'HelloWorld';
generate(abi: Abi, fileName: string, absPathToABIs: string, absPathToOutput: string): void {
generate(abi, fileName, absPathToOutput, absPathToABIs);
}
}
Then you need to add your plugin to the list of plugins:
typechain.loadPlugins(new MyPlugin());
Or you can load them via cli:
npx @727-ventures/typechain-polkadot --input path/to/abis --output path/to/output --plugins ./plugins-directory
Note: if you're using the cli, every plugin should end with
.plugin.ts
and have default export of the plugin itself.
Also you can use loadPluginsFromFiles
method to load plugins from files:
typechain.loadPluginsFromFiles(
'./plugins-directory'
)
Example of plugins usage
You can find an example of plugins usage in the examples directory.