Skip to content

Commit e8d6e46

Browse files
authored
[FAB-18228] Add ERC20 fungible token sample for chaincode javascript (hyperledger#327)
This PR adds ERC20 capabilities to the token-account-based sample. It includes javascript Chaincode and the updated README to explain how to use tokens in the Fabric test-network. Signed-off-by: Yuki Kondo <[email protected]>
1 parent 3fbce62 commit e8d6e46

File tree

9 files changed

+1029
-0
lines changed

9 files changed

+1029
-0
lines changed

Diff for: token-account-based/README.md

+150
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,17 @@ The -ca flag is used to deploy the network using certificate authorities. This a
3030
## Deploy the smart contract to the channel
3131

3232
You can use the test network script to deploy the account-based token contract to the channel that was just created. Deploy the smart contract to `mychannel` using the following command:
33+
34+
**For a Go Contract:**
3335
```
3436
./network.sh deployCC -ccn token_account -ccp ../token-account-based/chaincode-go/
3537
```
3638

39+
**For a JavaScript Contract:**
40+
```
41+
./network.sh deployCC -ccn token_account -ccp ../token-account-based/chaincode-javascript/ -ccl javascript
42+
```
43+
3744
The above command deploys the go chaincode with short name `token_account`. The smart contract will use the default endorsement policy of majority of channel members.
3845
Since the channel has two members, this implies that we'll need to get peer endorsements from 2 out of the 2 channel members.
3946

@@ -140,6 +147,8 @@ Using the Org2 terminal, the Org2 recipient user can retrieve their own account
140147
peer chaincode query -C mychannel -n token_account -c '{"function":"ClientAccountID","Args":[]}'
141148
```
142149

150+
**For a Go Contract:**
151+
143152
The function returns of recipient's account ID:
144153
```
145154
eDUwOTo6Q049cmVjaXBpZW50LE9VPWNsaWVudCxPPUh5cGVybGVkZ2VyLFNUPU5vcnRoIENhcm9saW5hLEM9VVM6OkNOPWNhLm9yZzIuZXhhbXBsZS5jb20sTz1vcmcyLmV4YW1wbGUuY29tLEw9SHVyc2xleSxTVD1IYW1wc2hpcmUsQz1VSw==
@@ -155,12 +164,28 @@ The result shows that the subject and issuer is indeed the recipient user from O
155164
x509::CN=recipient,OU=client,O=Hyperledger,ST=North Carolina,C=US::CN=ca.org2.example.com,O=org2.example.com,L=Hursley,ST=Hampshire,C=UK
156165
```
157166

167+
**For a JavaScript Contract:**
168+
169+
The function returns of recipient's client ID.
170+
The result shows that the subject and issuer is indeed the recipient user from Org2:
171+
```
172+
x509::/C=US/ST=North Carolina/O=Hyperledger/OU=client/CN=recipient::/C=UK/ST=Hampshire/L=Hursley/O=org2.example.com/CN=ca.org2.example.com
173+
```
174+
158175
After the Org2 recipient provides their account ID to the minter, the minter can initiate a transfer from their account to the recipient's account.
159176
Back in the Org1 terminal, request the transfer of 100 tokens to the recipient account:
177+
178+
**For a Go Contract:**
160179
```
161180
peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n token_account -c '{"function":"Transfer","Args":[ "eDUwOTo6Q049cmVjaXBpZW50LE9VPWNsaWVudCxPPUh5cGVybGVkZ2VyLFNUPU5vcnRoIENhcm9saW5hLEM9VVM6OkNOPWNhLm9yZzIuZXhhbXBsZS5jb20sTz1vcmcyLmV4YW1wbGUuY29tLEw9SHVyc2xleSxTVD1IYW1wc2hpcmUsQz1VSw==","100"]}'
162181
```
163182

183+
**For a JavaScript Contract:**
184+
```
185+
export RECIPIENT="x509::/C=US/ST=North Carolina/O=Hyperledger/OU=client/CN=recipient::/C=UK/ST=Hampshire/L=Hursley/O=org2.example.com/CN=ca.org2.example.com"
186+
peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n token_account -c '{"function":"Transfer","Args":[ "'"$RECIPIENT"'","100"]}'
187+
```
188+
164189
The `Transfer` function validates that the account associated with the calling client ID has sufficient funds for the transfer.
165190
It will then debit the caller's account and credit the recipient's account. Note that the sample contract will automatically create an account with zero balance for the recipient, if one does not yet exist.
166191

@@ -186,6 +211,131 @@ The function queries the balance of the account associated with the recipient cl
186211

187212
Congratulations, you've transferred 100 tokens! The Org2 recipient can now transfer tokens to other registered users in the same manner.
188213

214+
## Another scenario (for JavaScript contract only)
215+
216+
This sample has another transfer method called `transferFrom`, which allows an approved spender to transfer fungible tokens on behalf of the account owner. The second scenario demonstrates how to approve the spender and transfer fungible tokens.
217+
218+
In this tutorial, you will approve the spender and transfer tokens as follows:
219+
220+
- A minter has already created tokens according to the scenario above.
221+
- The same minter client uses the `approve` function to set the allowance of tokens a spender client can transfer on behalf of the minter. It is assumed that the spender has provided their client ID to the `approve` caller out of band.
222+
- The spender client will then use the `transferFrom` function to transfer the requested number of tokens to the recipient's account on behalf of the minter. It is assumed that the recipient has provided their client ID to the `transferFrom` caller out of band.
223+
224+
## Register identities
225+
226+
You have already brought up the network and deployed the smart contract to the channel. We will use the same network and smart contract.
227+
228+
We will use the Org1 CA to create the spender identity.
229+
Back in the Org1 terminal, you can register a new spender client identity using the `fabric-ca-client` tool:
230+
```
231+
fabric-ca-client register --caname ca-org1 --id.name spender --id.secret spenderpw --id.type client --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem
232+
```
233+
234+
You can now generate the identity certificates and MSP folder by providing the enroll name and secret to the enroll command:
235+
```
236+
fabric-ca-client enroll -u https://spender:spenderpw@localhost:7054 --caname ca-org1 -M ${PWD}/organizations/peerOrganizations/org1.example.com/users/[email protected]/msp --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem
237+
```
238+
239+
Run the command below to copy the Node OU configuration file into the spender identity MSP folder.
240+
```
241+
cp ${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org1.example.com/users/[email protected]/msp/config.yaml
242+
```
243+
244+
## Approve a spender
245+
246+
The minter intends to approve 500 tokens to be transferred by the spender, but first the spender needs to provide their own client ID as the payment address.
247+
248+
Open a 3rd terminal to represent the spender in Org1 and navigate to fabric-samples/test-network. Set the the environment variables for the Org1 spender user.
249+
250+
```
251+
export PATH=${PWD}/../bin:${PWD}:$PATH
252+
export FABRIC_CFG_PATH=$PWD/../config/
253+
export CORE_PEER_TLS_ENABLED=true
254+
export CORE_PEER_LOCALMSPID="Org1MSP"
255+
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/[email protected]/msp
256+
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
257+
export CORE_PEER_ADDRESS=localhost:7051
258+
export TARGET_TLS_OPTIONS="-o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt"
259+
```
260+
261+
Now the Org1 spender can retrieve their own client ID:
262+
```
263+
peer chaincode query -C mychannel -n token_account -c '{"function":"ClientAccountID","Args":[]}'
264+
```
265+
266+
The function returns of spender's client ID.
267+
The result shows that the subject and issuer is indeed the recipient user from Org2:
268+
```
269+
x509::/C=US/ST=North Carolina/O=Hyperledger/OU=client/CN=spender::/C=US/ST=North Carolina/L=Durham/O=org1.example.com/CN=ca.org1.example.com
270+
```
271+
272+
After the Org1 spender provides their client ID to the minter, the minter can approve a spender.
273+
Back in the Org1 terminal, request the approval of 100 tokens to be withdrew by the spender.:
274+
```
275+
export SPENDER="x509::/C=US/ST=North Carolina/O=Hyperledger/OU=client/CN=spender::/C=US/ST=North Carolina/L=Durham/O=org1.example.com/CN=ca.org1.example.com"
276+
peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n token_account -c '{"function":"Approve","Args":["'"$SPENDER"'", "500"]}'
277+
```
278+
279+
The approve function added that the spender client can consume 500 tokens on behalf of the minter. We can check the spender client's allowance from the minter by calling the `allowance` function.
280+
281+
Let's request the spender's allowance from the minter:
282+
```
283+
export MINTER="x509::/C=US/ST=North Carolina/O=Hyperledger/OU=client/CN=minter::/C=US/ST=North Carolina/L=Durham/O=org1.example.com/CN=ca.org1.example.com"
284+
peer chaincode query -C mychannel -n token_account -c '{"function":"Allowance","Args":["'"$MINTER"'", "'"$SPENDER"'"]}'
285+
```
286+
287+
The function queries the allowance associated with the spender client ID and returns:
288+
```
289+
500
290+
```
291+
292+
## TransferFrom tokens
293+
294+
The spender intends to transfer 100 tokens to the Org2 recipient on behalf of the minter. The spender has already got the minter client Id and the recipient client ID.
295+
296+
Back in the 3rd terminal, request the transfer of 100 tokens to the recipient account:
297+
```
298+
export MINTER="x509::/C=US/ST=North Carolina/O=Hyperledger/OU=client/CN=minter::/C=US/ST=North Carolina/L=Durham/O=org1.example.com/CN=ca.org1.example.com"
299+
export RECIPIENT="x509::/C=US/ST=North Carolina/O=Hyperledger/OU=client/CN=recipient::/C=UK/ST=Hampshire/L=Hursley/O=org2.example.com/CN=ca.org2.example.com"
300+
peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n token_account -c '{"function":"TransferFrom","Args":[ "'"$MINTER"'", "'"$RECIPIENT"'", "100"]}'
301+
```
302+
303+
The `TransferFrom` function has three args: sender, recipient, amount. The function validates that the account associated with the sender has sufficient funds for the transfer. The function also validates if the allowance associated with the calling client ID exceeds funds to be transferred.
304+
It will then debit the sender's account and credit the recipient's account. It will also decrease the spender's allowance approved by the minter. Note that the sample contract will automatically create an account with zero balance for the recipient, if one does not yet exist.
305+
306+
While still in the 3rd terminal, let's request the minter's account balance again:
307+
```
308+
peer chaincode query -C mychannel -n token_account -c '{"function":"BalanceOf","Args":["'"$MINTER"'"]}'
309+
```
310+
311+
The function queries the balance of the account associated with the minter client ID and returns:
312+
```
313+
4800
314+
```
315+
316+
While still in the 3rd terminal, let's request the spender's allowance from the minter again:
317+
```
318+
export SPENDER="x509::/C=US/ST=North Carolina/O=Hyperledger/OU=client/CN=spender::/C=US/ST=North Carolina/L=Durham/O=org1.example.com/CN=ca.org1.example.com"
319+
peer chaincode query -C mychannel -n token_account -c '{"function":"Allowance","Args":["'"$MINTER"'", "'"$SPENDER"'"]}'
320+
```
321+
322+
The function queries the allowance associated with the spender client ID and returns:
323+
```
324+
400
325+
```
326+
327+
And then using the Org2 terminal, let's request the recipient's balance:
328+
```
329+
peer chaincode query -C mychannel -n token_account -c '{"function":"ClientAccountBalance","Args":[]}'
330+
```
331+
332+
The function queries the balance of the account associated with the recipient client ID and returns:
333+
```
334+
200
335+
```
336+
337+
Congratulations, you've transferred 100 tokens! The Org2 recipient can now transfer tokens to other registered users in the same manner.
338+
189339
## Clean up
190340

191341
When you are finished, you can bring down the test network. The command will remove all the nodes of the test network, and delete any ledger data that you created:
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
5+
root = true
6+
7+
[*]
8+
indent_style = space
9+
indent_size = 4
10+
end_of_line = lf
11+
charset = utf-8
12+
trim_trailing_whitespace = true
13+
insert_final_newline = true
14+
15+
[*.md]
16+
trim_trailing_whitespace = false
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
5+
coverage
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*/
4+
5+
module.exports = {
6+
env: {
7+
node: true,
8+
es6: true,
9+
mocha: true
10+
},
11+
parserOptions: {
12+
ecmaVersion: 8,
13+
sourceType: 'script'
14+
},
15+
extends: "eslint:recommended",
16+
rules: {
17+
indent: ['error', 4],
18+
'linebreak-style': ['error', 'unix'],
19+
quotes: ['error', 'single'],
20+
semi: ['error', 'always'],
21+
'no-unused-vars': ['error', { args: 'none' }],
22+
'no-console': 'off',
23+
curly: 'error',
24+
eqeqeq: 'error',
25+
'no-throw-literal': 'error',
26+
strict: 'error',
27+
'no-var': 'error',
28+
'dot-notation': 'error',
29+
'no-tabs': 'error',
30+
'no-trailing-spaces': 'error',
31+
'no-use-before-define': 'error',
32+
'no-useless-call': 'error',
33+
'no-with': 'error',
34+
'operator-linebreak': 'error',
35+
yoda: 'error',
36+
'quote-props': ['error', 'as-needed'],
37+
'no-constant-condition': ["error", { "checkLoops": false }]
38+
}
39+
};

Diff for: token-account-based/chaincode-javascript/.gitignore

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
5+
# Logs
6+
logs
7+
*.log
8+
npm-debug.log*
9+
yarn-debug.log*
10+
yarn-error.log*
11+
12+
# Runtime data
13+
pids
14+
*.pid
15+
*.seed
16+
*.pid.lock
17+
18+
# Directory for instrumented libs generated by jscoverage/JSCover
19+
lib-cov
20+
21+
# Coverage directory used by tools like istanbul
22+
coverage
23+
24+
# nyc test coverage
25+
.nyc_output
26+
27+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
28+
.grunt
29+
30+
# Bower dependency directory (https://bower.io/)
31+
bower_components
32+
33+
# node-waf configuration
34+
.lock-wscript
35+
36+
# Compiled binary addons (https://nodejs.org/api/addons.html)
37+
build/Release
38+
39+
# Dependency directories
40+
node_modules/
41+
jspm_packages/
42+
package-lock.json
43+
44+
# TypeScript v1 declaration files
45+
typings/
46+
47+
# Optional npm cache directory
48+
.npm
49+
50+
# Optional eslint cache
51+
.eslintcache
52+
53+
# Optional REPL history
54+
.node_repl_history
55+
56+
# Output of 'npm pack'
57+
*.tgz
58+
59+
# Yarn Integrity file
60+
.yarn-integrity
61+
62+
# dotenv environment variables file
63+
.env
64+
65+
# parcel-bundler cache (https://parceljs.org/)
66+
.cache
67+
68+
# next.js build output
69+
.next
70+
71+
# nuxt.js build output
72+
.nuxt
73+
74+
# vuepress build output
75+
.vuepress/dist
76+
77+
# Serverless directories
78+
.serverless

Diff for: token-account-based/chaincode-javascript/index.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
SPDX-License-Identifier: Apache-2.0
3+
*/
4+
5+
'use strict';
6+
7+
const tokenERC20Contract = require('./lib/tokenERC20.js');
8+
9+
module.exports.TokenERC20Contract = tokenERC20Contract;
10+
module.exports.contracts = [tokenERC20Contract];

0 commit comments

Comments
 (0)