Fabric Contract Client
Contract's should be written in a way that is clean, simple and easy to maintain.
For this, I was a big fan of Convector
But given Convector is not likely to be maintained or support Fabric 2.0, I needed to find and maintain a replacement which allows me to write cleaner code.
I noticed that the fabric-contract-api
exists to write Chaincode but it has no client support.
This leverages your fabric-contract-api Chaincodes to extend and support them for usage at the Client end thus helping you write cleaner and easy to maintain code.
Usage
Include the library first
import ContractClient from '@pratikpc/fabric-contract-client';
You need to connect to the network first. This code remains same and is fairly simple.
ChainCodeContract is the type of the contract that you have deployed.
- My ChainCodeContract's actual name is
CarContract
- So you don't even need to know the actual name.
- The extraction is handled by the code.
- As long as you annotate via Info parameter which you should be doing anyways
- If not, you can pass the name of the contract as the 3rd parameter
// Use Library
const client = ContractClient(ChainCodeContract, network);
// Modern way to communicate with the library
const time = await client.returnCurrentTime();
console.log(
'Chaincode test result: Time is ',
time
);
// See how simple passing args can be
console.log(
`Chaincode says ${await client.sayHi('Pratik')}`
);
console.log('Transaction has been submitted');
Intellisense
When using Typescript or JavaScript, does this support Intellisense?
Yes of course it does.
Typescript Type Checks
Yes of course.
Older way
For those interested how it looked before this API was around
const contract =
network.getContract('chaincode');
console.log(
'Using traditional approach to query Hyperledger Fabric',
Number(
(
await contract.evaluateTransaction(
'returnCurrentTime'
)
).toString()
)
);
console.log(
'Using traditional approach to say hi to Hyperledger Fabric',
(
await contract.evaluateTransaction(
'sayHi',
'Pratik'
)
).toString()
);
This code:-
- Readability is less
- Expects every argument to be a String unlike ours
- Needs to convert Output from String unlike ours
Ours uses the value of your return argument - Needs you to know whether a transaction is submit or evaluate
- Requires you to remember the name of the Chaincode. Mine extracts it from the Info annotation.
Support for executing new and old way in the same code
Yes this support exists!
What's the catch?
The catch is that we expect you to write the code with proper and corect annotations
@Info({
title: 'CarContract',
DeployedChainCodeName: 'DeployedCarContract',
description: 'A sample Car Contract'
})
export default class CarContract extends Contract {
@Transaction(false)
@Returns('boolean')
// eslint-disable-next-line class-methods-use-this
public async carExists(
ctx: Context,
carId: string
): Promise<boolean> {
const data: Uint8Array = await ctx.stub.getState(
carId
);
return !!data && data.length > 0;
}
}
Contact me
You can create an issue here or contact me via LinkedIn