mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 07:25:10 +00:00
updated erc tokens samples (#731)
Signed-off-by: fraVlaca <ocsenarf@outlook.com>
This commit is contained in:
parent
2d32a8e7e3
commit
5f71466295
13 changed files with 1207 additions and 51 deletions
|
|
@ -23,6 +23,10 @@ const approvalPrefix = "account~operator"
|
|||
|
||||
const minterMSPID = "Org1MSP"
|
||||
|
||||
// Define key names for options
|
||||
const nameKey = "name"
|
||||
const symbolKey = "symbol"
|
||||
|
||||
// SmartContract provides functions for transferring tokens between accounts
|
||||
type SmartContract struct {
|
||||
contractapi.Contract
|
||||
|
|
@ -113,8 +117,17 @@ type ToID struct {
|
|||
// This function emits a TransferSingle event.
|
||||
func (s *SmartContract) Mint(ctx contractapi.TransactionContextInterface, account string, id uint64, amount uint64) error {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to mint new tokens
|
||||
err := authorizationHelper(ctx)
|
||||
err = authorizationHelper(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -140,12 +153,21 @@ func (s *SmartContract) Mint(ctx contractapi.TransactionContextInterface, accoun
|
|||
// This function emits a TransferBatch event.
|
||||
func (s *SmartContract) MintBatch(ctx contractapi.TransactionContextInterface, account string, ids []uint64, amounts []uint64) error {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
if len(ids) != len(amounts) {
|
||||
return fmt.Errorf("ids and amounts must have the same length")
|
||||
}
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to mint new tokens
|
||||
err := authorizationHelper(ctx)
|
||||
err = authorizationHelper(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -160,7 +182,10 @@ func (s *SmartContract) MintBatch(ctx contractapi.TransactionContextInterface, a
|
|||
amountToSend := make(map[uint64]uint64) // token id => amount
|
||||
|
||||
for i := 0; i < len(amounts); i++ {
|
||||
amountToSend[ids[i]] += amounts[i]
|
||||
amountToSend[ids[i]], err = add(amountToSend[ids[i]], amounts[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the map keys and sort it. This is necessary because iterating maps in Go is not deterministic
|
||||
|
|
@ -184,12 +209,21 @@ func (s *SmartContract) MintBatch(ctx contractapi.TransactionContextInterface, a
|
|||
// This function triggers a TransferSingle event.
|
||||
func (s *SmartContract) Burn(ctx contractapi.TransactionContextInterface, account string, id uint64, amount uint64) error {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
if account == "0x0" {
|
||||
return fmt.Errorf("burn to the zero address")
|
||||
}
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to burn new tokens
|
||||
err := authorizationHelper(ctx)
|
||||
err = authorizationHelper(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -214,6 +248,15 @@ func (s *SmartContract) Burn(ctx contractapi.TransactionContextInterface, accoun
|
|||
// This function emits a TransferBatch event.
|
||||
func (s *SmartContract) BurnBatch(ctx contractapi.TransactionContextInterface, account string, ids []uint64, amounts []uint64) error {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
if account == "0x0" {
|
||||
return fmt.Errorf("burn to the zero address")
|
||||
}
|
||||
|
|
@ -223,7 +266,7 @@ func (s *SmartContract) BurnBatch(ctx contractapi.TransactionContextInterface, a
|
|||
}
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to burn new tokens
|
||||
err := authorizationHelper(ctx)
|
||||
err = authorizationHelper(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -247,6 +290,16 @@ func (s *SmartContract) BurnBatch(ctx contractapi.TransactionContextInterface, a
|
|||
// recipient account must be a valid clientID as returned by the ClientID() function
|
||||
// This function triggers a TransferSingle event
|
||||
func (s *SmartContract) TransferFrom(ctx contractapi.TransactionContextInterface, sender string, recipient string, id uint64, amount uint64) error {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
if sender == recipient {
|
||||
return fmt.Errorf("transfer to self")
|
||||
}
|
||||
|
|
@ -293,6 +346,16 @@ func (s *SmartContract) TransferFrom(ctx contractapi.TransactionContextInterface
|
|||
// recipient account must be a valid clientID as returned by the ClientID() function
|
||||
// This function triggers a TransferBatch event
|
||||
func (s *SmartContract) BatchTransferFrom(ctx contractapi.TransactionContextInterface, sender string, recipient string, ids []uint64, amounts []uint64) error {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
if sender == recipient {
|
||||
return fmt.Errorf("transfer to self")
|
||||
}
|
||||
|
|
@ -332,7 +395,10 @@ func (s *SmartContract) BatchTransferFrom(ctx contractapi.TransactionContextInte
|
|||
amountToSend := make(map[uint64]uint64) // token id => amount
|
||||
|
||||
for i := 0; i < len(amounts); i++ {
|
||||
amountToSend[ids[i]] += amounts[i]
|
||||
amountToSend[ids[i]], err = add(amountToSend[ids[i]], amounts[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the map keys and sort it. This is necessary because iterating maps in Go is not deterministic
|
||||
|
|
@ -356,6 +422,15 @@ func (s *SmartContract) BatchTransferFrom(ctx contractapi.TransactionContextInte
|
|||
// This function triggers a TransferBatchMultiRecipient event
|
||||
func (s *SmartContract) BatchTransferFromMultiRecipient(ctx contractapi.TransactionContextInterface, sender string, recipients []string, ids []uint64, amounts []uint64) error {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
if len(recipients) != len(ids) || len(ids) != len(amounts) {
|
||||
return fmt.Errorf("recipients, ids, and amounts must have the same length")
|
||||
}
|
||||
|
|
@ -393,7 +468,10 @@ func (s *SmartContract) BatchTransferFromMultiRecipient(ctx contractapi.Transact
|
|||
amountToSend := make(map[ToID]uint64) // (recipient, id ) => amount
|
||||
|
||||
for i := 0; i < len(amounts); i++ {
|
||||
amountToSend[ToID{recipients[i], ids[i]}] += amounts[i]
|
||||
amountToSend[ToID{recipients[i], ids[i]}], err = add(amountToSend[ToID{recipients[i], ids[i]}], amounts[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the map keys and sort it. This is necessary because iterating maps in Go is not deterministic
|
||||
|
|
@ -425,6 +503,16 @@ func (s *SmartContract) IsApprovedForAll(ctx contractapi.TransactionContextInter
|
|||
|
||||
// _isApprovedForAll returns true if operator is approved to transfer account's tokens.
|
||||
func _isApprovedForAll(ctx contractapi.TransactionContextInterface, account string, operator string) (bool, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return false, fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
approvalKey, err := ctx.GetStub().CreateCompositeKey(approvalPrefix, []string{account, operator})
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to create the composite key for prefix %s: %v", approvalPrefix, err)
|
||||
|
|
@ -450,6 +538,16 @@ func _isApprovedForAll(ctx contractapi.TransactionContextInterface, account stri
|
|||
|
||||
// SetApprovalForAll returns true if operator is approved to transfer account's tokens.
|
||||
func (s *SmartContract) SetApprovalForAll(ctx contractapi.TransactionContextInterface, operator string, approved bool) error {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Get ID of submitting client identity
|
||||
account, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
|
|
@ -490,11 +588,31 @@ func (s *SmartContract) SetApprovalForAll(ctx contractapi.TransactionContextInte
|
|||
|
||||
// BalanceOf returns the balance of the given account
|
||||
func (s *SmartContract) BalanceOf(ctx contractapi.TransactionContextInterface, account string, id uint64) (uint64, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return 0, fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
return balanceOfHelper(ctx, account, id)
|
||||
}
|
||||
|
||||
// BalanceOfBatch returns the balance of multiple account/token pairs
|
||||
func (s *SmartContract) BalanceOfBatch(ctx contractapi.TransactionContextInterface, accounts []string, ids []uint64) ([]uint64, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return nil, fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
if len(accounts) != len(ids) {
|
||||
return nil, fmt.Errorf("accounts and ids must have the same length")
|
||||
}
|
||||
|
|
@ -515,6 +633,15 @@ func (s *SmartContract) BalanceOfBatch(ctx contractapi.TransactionContextInterfa
|
|||
// ClientAccountBalance returns the balance of the requesting client's account
|
||||
func (s *SmartContract) ClientAccountBalance(ctx contractapi.TransactionContextInterface, id uint64) (uint64, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return 0, fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Get ID of submitting client identity
|
||||
clientID, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
|
|
@ -529,6 +656,15 @@ func (s *SmartContract) ClientAccountBalance(ctx contractapi.TransactionContextI
|
|||
// Users can use this function to get their own account id, which they can then give to others as the payment address
|
||||
func (s *SmartContract) ClientAccountID(ctx contractapi.TransactionContextInterface) (string, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "", fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Get ID of submitting client identity
|
||||
clientAccountID, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
|
|
@ -542,8 +678,17 @@ func (s *SmartContract) ClientAccountID(ctx contractapi.TransactionContextInterf
|
|||
// This function triggers URI event for each token id
|
||||
func (s *SmartContract) SetURI(ctx contractapi.TransactionContextInterface, uri string) error {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to mint new tokens
|
||||
err := authorizationHelper(ctx)
|
||||
err = authorizationHelper(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -563,6 +708,15 @@ func (s *SmartContract) SetURI(ctx contractapi.TransactionContextInterface, uri
|
|||
// URI returns the URI
|
||||
func (s *SmartContract) URI(ctx contractapi.TransactionContextInterface, id uint64) (string, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "", fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
uriBytes, err := ctx.GetStub().GetState(uriKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get uri: %v", err)
|
||||
|
|
@ -577,8 +731,17 @@ func (s *SmartContract) URI(ctx contractapi.TransactionContextInterface, id uint
|
|||
|
||||
func (s *SmartContract) BroadcastTokenExistance(ctx contractapi.TransactionContextInterface, id uint64) error {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to mint new tokens
|
||||
err := authorizationHelper(ctx)
|
||||
err = authorizationHelper(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -594,6 +757,84 @@ func (s *SmartContract) BroadcastTokenExistance(ctx contractapi.TransactionConte
|
|||
return emitTransferSingle(ctx, transferSingleEvent)
|
||||
}
|
||||
|
||||
// Name returns a descriptive name for fungible tokens in this contract
|
||||
// returns {String} Returns the name of the token
|
||||
|
||||
func (s *SmartContract) Name(ctx contractapi.TransactionContextInterface) (string, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "", fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
bytes, err := ctx.GetStub().GetState(nameKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get Name bytes: %s", err)
|
||||
}
|
||||
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
// Symbol returns an abbreviated name for fungible tokens in this contract.
|
||||
// returns {String} Returns the symbol of the token
|
||||
|
||||
func (s *SmartContract) Symbol(ctx contractapi.TransactionContextInterface) (string, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "", fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
bytes, err := ctx.GetStub().GetState(symbolKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get Symbol: %v", err)
|
||||
}
|
||||
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
// Set information for a token and intialize contract.
|
||||
// param {String} name The name of the token
|
||||
// param {String} symbol The symbol of the token
|
||||
func (s *SmartContract) Initialize(ctx contractapi.TransactionContextInterface, name string, symbol string) (bool, error) {
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to intitialize contract
|
||||
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get MSPID: %v", err)
|
||||
} else if clientMSPID != "Org1MSP" {
|
||||
return false, fmt.Errorf("client is not authorized to initialize contract")
|
||||
}
|
||||
|
||||
//check contract options are not already set, client is not authorized to change them once intitialized
|
||||
bytes, err := ctx.GetStub().GetState(nameKey)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get Name: %v", err)
|
||||
} else if bytes != nil {
|
||||
return false, fmt.Errorf("contract options are already set, client is not authorized to change them")
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(nameKey, []byte(name))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to set token name: %v", err)
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(symbolKey, []byte(symbol))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to set symbol: %v", err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Helper Functions
|
||||
|
||||
// authorizationHelper checks minter authorization - this sample assumes Org1 is the central banker with privilege to mint new tokens
|
||||
|
|
@ -646,7 +887,10 @@ func addBalance(ctx contractapi.TransactionContextInterface, sender string, reci
|
|||
balance, _ = strconv.ParseUint(string(balanceBytes), 10, 64)
|
||||
}
|
||||
|
||||
balance += amount
|
||||
balance, err = add(balance, amount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(balanceKey, []byte(strconv.FormatUint(uint64(balance), 10)))
|
||||
if err != nil {
|
||||
|
|
@ -676,9 +920,13 @@ func setBalance(ctx contractapi.TransactionContextInterface, sender string, reci
|
|||
func removeBalance(ctx contractapi.TransactionContextInterface, sender string, ids []uint64, amounts []uint64) error {
|
||||
// Calculate the total amount of each token to withdraw
|
||||
necessaryFunds := make(map[uint64]uint64) // token id -> necessary amount
|
||||
var err error
|
||||
|
||||
for i := 0; i < len(amounts); i++ {
|
||||
necessaryFunds[ids[i]] += amounts[i]
|
||||
necessaryFunds[ids[i]], err = add(necessaryFunds[ids[i]], amounts[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the map keys and sort it. This is necessary because iterating maps in Go is not deterministic
|
||||
|
|
@ -708,7 +956,10 @@ func removeBalance(ctx contractapi.TransactionContextInterface, sender string, i
|
|||
}
|
||||
|
||||
partBalAmount, _ := strconv.ParseUint(string(queryResponse.Value), 10, 64)
|
||||
partialBalance += partBalAmount
|
||||
partialBalance, err = add(partialBalance, partBalAmount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, compositeKeyParts, err := ctx.GetStub().SplitCompositeKey(queryResponse.Key)
|
||||
if err != nil {
|
||||
|
|
@ -730,7 +981,11 @@ func removeBalance(ctx contractapi.TransactionContextInterface, sender string, i
|
|||
return fmt.Errorf("sender has insufficient funds for token %v, needed funds: %v, available fund: %v", tokenId, neededAmount, partialBalance)
|
||||
} else if partialBalance > neededAmount {
|
||||
// Send the remainder back to the sender
|
||||
remainder := partialBalance - neededAmount
|
||||
remainder, err := sub(partialBalance, neededAmount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if selfRecipientKeyNeedsToBeRemoved {
|
||||
// Set balance for the key that has the same address for sender and recipient
|
||||
err = setBalance(ctx, sender, sender, tokenId, remainder)
|
||||
|
|
@ -821,7 +1076,10 @@ func balanceOfHelper(ctx contractapi.TransactionContextInterface, account string
|
|||
}
|
||||
|
||||
balAmount, _ := strconv.ParseUint(string(queryResponse.Value), 10, 64)
|
||||
balance += balAmount
|
||||
balance, err = add(balance, balAmount)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return balance, nil
|
||||
|
|
@ -859,3 +1117,42 @@ func sortedKeysToID(m map[ToID]uint64) []ToID {
|
|||
})
|
||||
return keys
|
||||
}
|
||||
|
||||
//Checks that contract options have been already initialized
|
||||
func checkInitialized(ctx contractapi.TransactionContextInterface) (bool, error) {
|
||||
tokenName, err := ctx.GetStub().GetState(nameKey)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get token name: %v", err)
|
||||
} else if tokenName == nil {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// add two number checking for overflow
|
||||
func add(b uint64, q uint64) (uint64, error) {
|
||||
|
||||
// Check overflow
|
||||
var sum uint64
|
||||
sum = q + b
|
||||
|
||||
if sum < q {
|
||||
return 0, fmt.Errorf("Math: addition overflow occurred %d + %d", b, q)
|
||||
}
|
||||
|
||||
return sum, nil
|
||||
}
|
||||
|
||||
// sub two number checking for overflow
|
||||
func sub(b uint64, q uint64) (uint64, error) {
|
||||
|
||||
// Check overflow
|
||||
var diff uint64
|
||||
diff = q - b
|
||||
|
||||
if diff > q {
|
||||
return 0, fmt.Errorf("Math: subtraction overflow occurred %d - %d", b, q)
|
||||
}
|
||||
|
||||
return diff, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,11 +11,16 @@ import (
|
|||
)
|
||||
|
||||
// Define key names for options
|
||||
const nameKey = "name"
|
||||
const symbolKey = "symbol"
|
||||
const decimalsKey = "decimals"
|
||||
const totalSupplyKey = "totalSupply"
|
||||
|
||||
// Define objectType names for prefix
|
||||
const allowancePrefix = "allowance"
|
||||
|
||||
// Define key names for options
|
||||
|
||||
|
||||
// SmartContract provides functions for transferring tokens between accounts
|
||||
type SmartContract struct {
|
||||
contractapi.Contract
|
||||
|
|
@ -32,6 +37,15 @@ type event struct {
|
|||
// This function triggers a Transfer event
|
||||
func (s *SmartContract) Mint(ctx contractapi.TransactionContextInterface, amount int) error {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to mint new tokens
|
||||
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
||||
if err != nil {
|
||||
|
|
@ -65,7 +79,10 @@ func (s *SmartContract) Mint(ctx contractapi.TransactionContextInterface, amount
|
|||
currentBalance, _ = strconv.Atoi(string(currentBalanceBytes)) // Error handling not needed since Itoa() was used when setting the account balance, guaranteeing it was an integer.
|
||||
}
|
||||
|
||||
updatedBalance := currentBalance + amount
|
||||
updatedBalance,err := add(currentBalance, amount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(minter, []byte(strconv.Itoa(updatedBalance)))
|
||||
if err != nil {
|
||||
|
|
@ -88,7 +105,11 @@ func (s *SmartContract) Mint(ctx contractapi.TransactionContextInterface, amount
|
|||
}
|
||||
|
||||
// Add the mint amount to the total supply and update the state
|
||||
totalSupply += amount
|
||||
totalSupply, err = add(totalSupply, amount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(totalSupplyKey, []byte(strconv.Itoa(totalSupply)))
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -114,6 +135,14 @@ func (s *SmartContract) Mint(ctx contractapi.TransactionContextInterface, amount
|
|||
// This function triggers a Transfer event
|
||||
func (s *SmartContract) Burn(ctx contractapi.TransactionContextInterface, amount int) error {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to burn new tokens
|
||||
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
||||
if err != nil {
|
||||
|
|
@ -147,7 +176,10 @@ func (s *SmartContract) Burn(ctx contractapi.TransactionContextInterface, amount
|
|||
|
||||
currentBalance, _ = strconv.Atoi(string(currentBalanceBytes)) // Error handling not needed since Itoa() was used when setting the account balance, guaranteeing it was an integer.
|
||||
|
||||
updatedBalance := currentBalance - amount
|
||||
updatedBalance, err := sub(currentBalance, amount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(minter, []byte(strconv.Itoa(updatedBalance)))
|
||||
if err != nil {
|
||||
|
|
@ -168,7 +200,11 @@ func (s *SmartContract) Burn(ctx contractapi.TransactionContextInterface, amount
|
|||
totalSupply, _ := strconv.Atoi(string(totalSupplyBytes)) // Error handling not needed since Itoa() was used when setting the totalSupply, guaranteeing it was an integer.
|
||||
|
||||
// Subtract the burn amount to the total supply and update the state
|
||||
totalSupply -= amount
|
||||
totalSupply, err = sub(totalSupply, amount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(totalSupplyKey, []byte(strconv.Itoa(totalSupply)))
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -195,6 +231,15 @@ func (s *SmartContract) Burn(ctx contractapi.TransactionContextInterface, amount
|
|||
// This function triggers a Transfer event
|
||||
func (s *SmartContract) Transfer(ctx contractapi.TransactionContextInterface, recipient string, amount int) error {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Get ID of submitting client identity
|
||||
clientID, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
|
|
@ -222,6 +267,16 @@ func (s *SmartContract) Transfer(ctx contractapi.TransactionContextInterface, re
|
|||
|
||||
// BalanceOf returns the balance of the given account
|
||||
func (s *SmartContract) BalanceOf(ctx contractapi.TransactionContextInterface, account string) (int, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return 0, fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
balanceBytes, err := ctx.GetStub().GetState(account)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to read from world state: %v", err)
|
||||
|
|
@ -238,6 +293,15 @@ func (s *SmartContract) BalanceOf(ctx contractapi.TransactionContextInterface, a
|
|||
// ClientAccountBalance returns the balance of the requesting client's account
|
||||
func (s *SmartContract) ClientAccountBalance(ctx contractapi.TransactionContextInterface) (int, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return 0, fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Get ID of submitting client identity
|
||||
clientID, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
|
|
@ -262,6 +326,15 @@ func (s *SmartContract) ClientAccountBalance(ctx contractapi.TransactionContextI
|
|||
// Users can use this function to get their own account id, which they can then give to others as the payment address
|
||||
func (s *SmartContract) ClientAccountID(ctx contractapi.TransactionContextInterface) (string, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "", fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Get ID of submitting client identity
|
||||
clientAccountID, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
|
|
@ -274,6 +347,15 @@ func (s *SmartContract) ClientAccountID(ctx contractapi.TransactionContextInterf
|
|||
// TotalSupply returns the total token supply
|
||||
func (s *SmartContract) TotalSupply(ctx contractapi.TransactionContextInterface) (int, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return 0, fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Retrieve total supply of tokens from state of smart contract
|
||||
totalSupplyBytes, err := ctx.GetStub().GetState(totalSupplyKey)
|
||||
if err != nil {
|
||||
|
|
@ -299,6 +381,15 @@ func (s *SmartContract) TotalSupply(ctx contractapi.TransactionContextInterface)
|
|||
// This function triggers an Approval event
|
||||
func (s *SmartContract) Approve(ctx contractapi.TransactionContextInterface, spender string, value int) error {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Get ID of submitting client identity
|
||||
owner, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
|
|
@ -336,6 +427,15 @@ func (s *SmartContract) Approve(ctx contractapi.TransactionContextInterface, spe
|
|||
// Allowance returns the amount still available for the spender to withdraw from the owner
|
||||
func (s *SmartContract) Allowance(ctx contractapi.TransactionContextInterface, owner string, spender string) (int, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return 0, fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Create allowanceKey
|
||||
allowanceKey, err := ctx.GetStub().CreateCompositeKey(allowancePrefix, []string{owner, spender})
|
||||
if err != nil {
|
||||
|
|
@ -366,6 +466,15 @@ func (s *SmartContract) Allowance(ctx contractapi.TransactionContextInterface, o
|
|||
// This function triggers a Transfer event
|
||||
func (s *SmartContract) TransferFrom(ctx contractapi.TransactionContextInterface, from string, to string, value int) error {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Get ID of submitting client identity
|
||||
spender, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
|
|
@ -399,7 +508,11 @@ func (s *SmartContract) TransferFrom(ctx contractapi.TransactionContextInterface
|
|||
}
|
||||
|
||||
// Decrease the allowance
|
||||
updatedAllowance := currentAllowance - value
|
||||
updatedAllowance, err := sub(currentAllowance, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(allowanceKey, []byte(strconv.Itoa(updatedAllowance)))
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -421,6 +534,90 @@ func (s *SmartContract) TransferFrom(ctx contractapi.TransactionContextInterface
|
|||
return nil
|
||||
}
|
||||
|
||||
// Name returns a descriptive name for fungible tokens in this contract
|
||||
// returns {String} Returns the name of the token
|
||||
|
||||
func (s *SmartContract) Name(ctx contractapi.TransactionContextInterface) (string, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "", fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
bytes, err := ctx.GetStub().GetState(nameKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get Name bytes: %s", err)
|
||||
}
|
||||
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
// Symbol returns an abbreviated name for fungible tokens in this contract.
|
||||
// returns {String} Returns the symbol of the token
|
||||
|
||||
func (s *SmartContract) Symbol(ctx contractapi.TransactionContextInterface) (string, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "", fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
bytes, err := ctx.GetStub().GetState(symbolKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get Symbol: %v", err)
|
||||
}
|
||||
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
// Set information for a token and intialize contract.
|
||||
// param {String} name The name of the token
|
||||
// param {String} symbol The symbol of the token
|
||||
// param {String} decimals The name of the token
|
||||
func (s *SmartContract) Initialize(ctx contractapi.TransactionContextInterface, name string, symbol string, decimals string) (bool, error) {
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to intitialize contract
|
||||
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get MSPID: %v", err)
|
||||
} else if clientMSPID != "Org1MSP" {
|
||||
return false, fmt.Errorf("client is not authorized to initialize contract")
|
||||
}
|
||||
|
||||
//check contract options are not already set, client is not authorized to change them once intitialized
|
||||
bytes, err := ctx.GetStub().GetState(nameKey)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get Name: %v", err)
|
||||
}else if bytes != nil {
|
||||
return false, fmt.Errorf("contract options are already set, client is not authorized to change them")
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(nameKey, []byte(name))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to set token name: %v", err)
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(symbolKey, []byte(symbol))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to set symbol: %v", err)
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(decimalsKey, []byte(decimals))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to set token name: %v", err)
|
||||
}
|
||||
|
||||
return true, nil;
|
||||
}
|
||||
|
||||
// Helper Functions
|
||||
|
||||
// transferHelper is a helper function that transfers tokens from the "from" address to the "to" address
|
||||
|
|
@ -463,8 +660,15 @@ func transferHelper(ctx contractapi.TransactionContextInterface, from string, to
|
|||
toCurrentBalance, _ = strconv.Atoi(string(toCurrentBalanceBytes)) // Error handling not needed since Itoa() was used when setting the account balance, guaranteeing it was an integer.
|
||||
}
|
||||
|
||||
fromUpdatedBalance := fromCurrentBalance - value
|
||||
toUpdatedBalance := toCurrentBalance + value
|
||||
fromUpdatedBalance, err := sub(fromCurrentBalance, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
toUpdatedBalance, err := add(toCurrentBalance, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(from, []byte(strconv.Itoa(fromUpdatedBalance)))
|
||||
if err != nil {
|
||||
|
|
@ -481,3 +685,44 @@ func transferHelper(ctx contractapi.TransactionContextInterface, from string, to
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// add two number checking for overflow
|
||||
func add(b int, q int) (int, error) {
|
||||
|
||||
// Check overflow
|
||||
var sum int
|
||||
sum = q + b
|
||||
|
||||
if (sum < q) == (b > 0 && q > 0) {
|
||||
return 0, fmt.Errorf("Math: addition overflow occurred %d + %d", b, q)
|
||||
}
|
||||
|
||||
return sum, nil
|
||||
}
|
||||
|
||||
//Checks that contract options have been already initialized
|
||||
func checkInitialized(ctx contractapi.TransactionContextInterface) (bool, error) {
|
||||
tokenName, err := ctx.GetStub().GetState(nameKey)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get token name: %v", err)
|
||||
}else if (tokenName == nil){
|
||||
return false , nil
|
||||
}
|
||||
return true , nil
|
||||
}
|
||||
|
||||
|
||||
// sub two number checking for overflow
|
||||
func sub(b int, q int) (int, error) {
|
||||
|
||||
// Check overflow
|
||||
var diff int
|
||||
diff = q - b
|
||||
|
||||
if (diff > q) == (b > 0 && q > 0) {
|
||||
return 0, fmt.Errorf("Math: Subtraction overflow occurred %d - %d", b, q)
|
||||
}
|
||||
|
||||
return diff, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ public enum ContractConstants {
|
|||
BALANCE_PREFIX("balance"),
|
||||
ALLOWANCE_PREFIX("allowance"),
|
||||
NAME_KEY("name"),
|
||||
SYMBOL_KEY("decimals"),
|
||||
DECIMALS_KEY("symbolKey"),
|
||||
SYMBOL_KEY("symbolKey"),
|
||||
DECIMALS_KEY("decimals"),
|
||||
TOTAL_SUPPLY_KEY("totalSupply"),
|
||||
TRANSFER_EVENT("Transfer"),
|
||||
MINTER_ORG_MSPID("Org1MSP"),
|
||||
|
|
|
|||
|
|
@ -76,6 +76,10 @@ public final class ERC20TokenContract implements ContractInterface {
|
|||
throw new ChaincodeException(
|
||||
"Client is not authorized to mint new tokens", UNAUTHORIZED_SENDER.toString());
|
||||
}
|
||||
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
|
||||
// Get ID of submitting client identity
|
||||
String minter = ctx.getClientIdentity().getId();
|
||||
if (amount <= 0) {
|
||||
|
|
@ -127,6 +131,10 @@ public final class ERC20TokenContract implements ContractInterface {
|
|||
throw new ChaincodeException(
|
||||
"Client is not authorized to burn tokens", UNAUTHORIZED_SENDER.toString());
|
||||
}
|
||||
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
|
||||
String minter = ctx.getClientIdentity().getId();
|
||||
if (amount <= 0) {
|
||||
throw new ChaincodeException(
|
||||
|
|
@ -173,6 +181,8 @@ public final class ERC20TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||
public void Transfer(final Context ctx, final String to, final long value) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
String from = ctx.getClientIdentity().getId();
|
||||
this.transferHelper(ctx, from, to, value);
|
||||
final Transfer transferEvent = new Transfer(from, to, value);
|
||||
|
|
@ -188,6 +198,8 @@ public final class ERC20TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public long BalanceOf(final Context ctx, final String owner) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
ChaincodeStub stub = ctx.getStub();
|
||||
CompositeKey balanceKey = stub.createCompositeKey(BALANCE_PREFIX.getValue(), owner);
|
||||
String balance = stub.getStringState(balanceKey.toString());
|
||||
|
|
@ -207,6 +219,8 @@ public final class ERC20TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public long ClientAccountBalance(final Context ctx) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
// Get ID of submitting client identity
|
||||
ChaincodeStub stub = ctx.getStub();
|
||||
String clientAccountID = ctx.getClientIdentity().getId();
|
||||
|
|
@ -230,6 +244,8 @@ public final class ERC20TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public String ClientAccountID(final Context ctx) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
// Get ID of submitting client identity
|
||||
return ctx.getClientIdentity().getId();
|
||||
}
|
||||
|
|
@ -242,6 +258,8 @@ public final class ERC20TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public long TotalSupply(final Context ctx) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
String totalSupply = ctx.getStub().getStringState(TOTAL_SUPPLY_KEY.getValue());
|
||||
if (stringIsNullOrEmpty(totalSupply)) {
|
||||
throw new ChaincodeException("Total Supply not found", NOT_FOUND.toString());
|
||||
|
|
@ -259,6 +277,8 @@ public final class ERC20TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||
public void Approve(final Context ctx, final String spender, final long value) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
ChaincodeStub stub = ctx.getStub();
|
||||
String owner = ctx.getClientIdentity().getId();
|
||||
CompositeKey allowanceKey =
|
||||
|
|
@ -282,6 +302,8 @@ public final class ERC20TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||
public long Allowance(final Context ctx, final String owner, final String spender) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
ChaincodeStub stub = ctx.getStub();
|
||||
CompositeKey allowanceKey =
|
||||
stub.createCompositeKey(ALLOWANCE_PREFIX.getValue(), owner, spender);
|
||||
|
|
@ -308,6 +330,8 @@ public final class ERC20TokenContract implements ContractInterface {
|
|||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||
public void TransferFrom(
|
||||
final Context ctx, final String from, final String to, final long value) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
String spender = ctx.getClientIdentity().getId();
|
||||
ChaincodeStub stub = ctx.getStub();
|
||||
// Retrieve the allowance of the spender
|
||||
|
|
@ -402,9 +426,23 @@ public final class ERC20TokenContract implements ContractInterface {
|
|||
* @param decimals The decimals of the token
|
||||
*/
|
||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||
public void SetOptions(
|
||||
public void Initialize(
|
||||
final Context ctx, final String name, final String symbol, final String decimals) {
|
||||
ChaincodeStub stub = ctx.getStub();
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to set Options for these tokens
|
||||
String clientMSPID = ctx.getClientIdentity().getMSPID();
|
||||
if (!clientMSPID.equalsIgnoreCase(ContractConstants.MINTER_ORG_MSPID.getValue())) {
|
||||
throw new ChaincodeException(
|
||||
"Client is not authorized to initialize contract", UNAUTHORIZED_SENDER.toString());
|
||||
}
|
||||
|
||||
//check contract options are not already set, client is not authorized to change them once intitialized
|
||||
String tokenName = stub.getStringState(ContractConstants.NAME_KEY.getValue());
|
||||
if (!stringIsNullOrEmpty(tokenName)) {
|
||||
throw new ChaincodeException("contract options are already set, client is not authorized to change them");
|
||||
}
|
||||
|
||||
stub.putStringState(NAME_KEY.getValue(), name);
|
||||
stub.putStringState(SYMBOL_KEY.getValue(), symbol);
|
||||
stub.putStringState(DECIMALS_KEY.getValue(), decimals);
|
||||
|
|
@ -420,6 +458,8 @@ public final class ERC20TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public String TokenName(final Context ctx) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
String tokenName = ctx.getStub().getStringState(ContractConstants.NAME_KEY.getValue());
|
||||
if (stringIsNullOrEmpty(tokenName)) {
|
||||
throw new ChaincodeException("Token name not found", NOT_FOUND.toString());
|
||||
|
|
@ -435,6 +475,8 @@ public final class ERC20TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public String TokenSymbol(final Context ctx) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
String tokenSymbol = ctx.getStub().getStringState(SYMBOL_KEY.getValue());
|
||||
if (stringIsNullOrEmpty(tokenSymbol)) {
|
||||
throw new ChaincodeException("Token symbol not found", NOT_FOUND.toString());
|
||||
|
|
@ -451,6 +493,8 @@ public final class ERC20TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public int Decimals(final Context ctx) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
String decimals = ctx.getStub().getStringState(DECIMALS_KEY.getValue());
|
||||
if (stringIsNullOrEmpty(decimals)) {
|
||||
throw new ChaincodeException("Decimal not found", NOT_FOUND.toString());
|
||||
|
|
@ -466,4 +510,17 @@ public final class ERC20TokenContract implements ContractInterface {
|
|||
private byte[] marshal(final Object obj) {
|
||||
return new Genson().serialize(obj).getBytes(UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that contract options have been already initialized
|
||||
*
|
||||
* @param ctx the transaction context
|
||||
* @return the number of decimals
|
||||
*/
|
||||
private void checkInitialized(final Context ctx) {
|
||||
String tokenName = ctx.getStub().getStringState(ContractConstants.NAME_KEY.getValue());
|
||||
if (stringIsNullOrEmpty(tokenName)) {
|
||||
throw new ChaincodeException("Contract options need to be set before calling any function, call Initialize() to initialize contract", NOT_FOUND.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ public class TokenERC20ContractTest {
|
|||
assertThat(thrown)
|
||||
.isInstanceOf(ChaincodeException.class)
|
||||
.hasNoCause()
|
||||
.hasMessage("Token name not found");
|
||||
.hasMessage("Contract options need to be set before calling any function, call Initialize() to initialize contract");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -67,6 +67,7 @@ public class TokenERC20ContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(stub.getStringState(SYMBOL_KEY.getValue())).thenReturn("ARBT");
|
||||
String toknName = contract.TokenSymbol(ctx);
|
||||
assertThat(toknName).isEqualTo("ARBT");
|
||||
|
|
@ -78,6 +79,7 @@ public class TokenERC20ContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(stub.getStringState(SYMBOL_KEY.getValue())).thenReturn("");
|
||||
Throwable thrown = catchThrowable(() -> contract.TokenSymbol(ctx));
|
||||
assertThat(thrown)
|
||||
|
|
@ -92,6 +94,7 @@ public class TokenERC20ContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(stub.getStringState(DECIMALS_KEY.getValue())).thenReturn("18");
|
||||
long decimal = contract.Decimals(ctx);
|
||||
assertThat(decimal).isEqualTo(18);
|
||||
|
|
@ -103,6 +106,7 @@ public class TokenERC20ContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(stub.getStringState(DECIMALS_KEY.getValue())).thenReturn("");
|
||||
Throwable thrown = catchThrowable(() -> contract.Decimals(ctx));
|
||||
assertThat(thrown)
|
||||
|
|
@ -117,6 +121,7 @@ public class TokenERC20ContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(stub.getStringState(TOTAL_SUPPLY_KEY.getValue())).thenReturn("222222222222");
|
||||
long totalSupply = contract.TotalSupply(ctx);
|
||||
assertThat(totalSupply).isEqualTo(222222222222L);
|
||||
|
|
@ -128,6 +133,7 @@ public class TokenERC20ContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(stub.getStringState(TOTAL_SUPPLY_KEY.getValue())).thenReturn("");
|
||||
Throwable thrown = catchThrowable(() -> contract.TotalSupply(ctx));
|
||||
assertThat(thrown)
|
||||
|
|
@ -143,6 +149,7 @@ public class TokenERC20ContractTest {
|
|||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
ClientIdentity ci = mock(ClientIdentity.class);
|
||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||
when(ci.getId()).thenReturn(org1UserId);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
|
|
@ -156,12 +163,15 @@ public class TokenERC20ContractTest {
|
|||
class TokenOperationsInvoke {
|
||||
|
||||
@Test
|
||||
public void invokeSetOptionsTest() {
|
||||
public void invokeInitializeTest() {
|
||||
ERC20TokenContract contract = new ERC20TokenContract();
|
||||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
contract.SetOptions(ctx, "ARBTToken", "ARBT", "18");
|
||||
ClientIdentity ci = mock(ClientIdentity.class);
|
||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||
contract.Initialize(ctx, "ARBTToken", "ARBT", "18");
|
||||
verify(stub).putStringState(NAME_KEY.getValue(), "ARBTToken");
|
||||
verify(stub).putStringState(SYMBOL_KEY.getValue(), "ARBT");
|
||||
verify(stub).putStringState(DECIMALS_KEY.getValue(), "18");
|
||||
|
|
@ -173,6 +183,7 @@ public class TokenERC20ContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
ClientIdentity ci = mock(ClientIdentity.class);
|
||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||
|
|
@ -192,6 +203,7 @@ public class TokenERC20ContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
ClientIdentity ci = mock(ClientIdentity.class);
|
||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||
|
|
@ -213,6 +225,7 @@ public class TokenERC20ContractTest {
|
|||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
ClientIdentity ci = mock(ClientIdentity.class);
|
||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||
when(ci.getId()).thenReturn(org1UserId);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
|
|
@ -234,6 +247,7 @@ public class TokenERC20ContractTest {
|
|||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
ClientIdentity ci = mock(ClientIdentity.class);
|
||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(ci.getMSPID()).thenReturn("Org2MSP");
|
||||
when(ci.getId()).thenReturn(org1UserId);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
|
|
@ -256,6 +270,7 @@ public class TokenERC20ContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
ClientIdentity ci = mock(ClientIdentity.class);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||
when(ci.getId()).thenReturn(org1UserId);
|
||||
|
|
@ -284,6 +299,7 @@ public class TokenERC20ContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
ClientIdentity ci = mock(ClientIdentity.class);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||
when(ci.getId()).thenReturn(org1UserId);
|
||||
|
|
@ -312,6 +328,7 @@ public class TokenERC20ContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
ClientIdentity ci = mock(ClientIdentity.class);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||
when(ci.getId()).thenReturn(org1UserId);
|
||||
|
|
@ -343,6 +360,7 @@ public class TokenERC20ContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
ClientIdentity ci = mock(ClientIdentity.class);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||
when(ci.getId()).thenReturn(org1UserId);
|
||||
|
|
@ -374,6 +392,7 @@ public class TokenERC20ContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
ClientIdentity ci = mock(ClientIdentity.class);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||
when(ci.getId()).thenReturn(org1UserId);
|
||||
|
|
@ -394,6 +413,7 @@ public class TokenERC20ContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
ClientIdentity ci = mock(ClientIdentity.class);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||
when(ci.getMSPID()).thenReturn("Org2MSP");
|
||||
when(ci.getId()).thenReturn(spender);
|
||||
|
|
@ -418,6 +438,7 @@ public class TokenERC20ContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
ClientIdentity ci = mock(ClientIdentity.class);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||
when(ci.getMSPID()).thenReturn("Org1MSP");
|
||||
when(ci.getId()).thenReturn(org1UserId);
|
||||
|
|
@ -446,6 +467,7 @@ public class TokenERC20ContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
ClientIdentity ci = mock(ClientIdentity.class);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||
when(ci.getId()).thenReturn(org1UserId);
|
||||
|
|
@ -465,6 +487,7 @@ public class TokenERC20ContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
ClientIdentity ci = mock(ClientIdentity.class);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||
when(ci.getId()).thenReturn(org1UserId);
|
||||
|
|
@ -489,6 +512,7 @@ public class TokenERC20ContractTest {
|
|||
+ " C=US::CN=ca.org2.example.com, O=org2.example.com, L=San Francisco, ST=California, C=US";
|
||||
|
||||
CompositeKey ckFromBalance = mock(CompositeKey.class);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(stub.createCompositeKey(BALANCE_PREFIX.toString(), org1UserId))
|
||||
.thenReturn(ckFromBalance);
|
||||
when(ckFromBalance.toString()).thenReturn(BALANCE_PREFIX.getValue() + org1UserId);
|
||||
|
|
@ -529,6 +553,7 @@ public class TokenERC20ContractTest {
|
|||
+ " C=US::CN=ca.org2.example.com, O=org2.example.com, L=San Francisco, ST=California, C=US";
|
||||
|
||||
CompositeKey ckFromBalance = mock(CompositeKey.class);
|
||||
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||
when(stub.createCompositeKey(BALANCE_PREFIX.getValue(), org1UserId))
|
||||
.thenReturn(ckFromBalance);
|
||||
when(ckFromBalance.toString()).thenReturn(BALANCE_PREFIX.getValue() + org1UserId);
|
||||
|
|
|
|||
|
|
@ -30,7 +30,12 @@ class TokenERC20Contract extends Contract {
|
|||
* @returns {String} Returns the name of the token
|
||||
*/
|
||||
async TokenName(ctx) {
|
||||
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const nameBytes = await ctx.stub.getState(nameKey);
|
||||
|
||||
return nameBytes.toString();
|
||||
}
|
||||
|
||||
|
|
@ -41,6 +46,10 @@ class TokenERC20Contract extends Contract {
|
|||
* @returns {String} Returns the symbol of the token
|
||||
*/
|
||||
async Symbol(ctx) {
|
||||
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const symbolBytes = await ctx.stub.getState(symbolKey);
|
||||
return symbolBytes.toString();
|
||||
}
|
||||
|
|
@ -53,6 +62,10 @@ class TokenERC20Contract extends Contract {
|
|||
* @returns {Number} Returns the number of decimals
|
||||
*/
|
||||
async Decimals(ctx) {
|
||||
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const decimalsBytes = await ctx.stub.getState(decimalsKey);
|
||||
const decimals = parseInt(decimalsBytes.toString());
|
||||
return decimals;
|
||||
|
|
@ -65,6 +78,10 @@ class TokenERC20Contract extends Contract {
|
|||
* @returns {Number} Returns the total token supply
|
||||
*/
|
||||
async TotalSupply(ctx) {
|
||||
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const totalSupplyBytes = await ctx.stub.getState(totalSupplyKey);
|
||||
const totalSupply = parseInt(totalSupplyBytes.toString());
|
||||
return totalSupply;
|
||||
|
|
@ -78,6 +95,10 @@ class TokenERC20Contract extends Contract {
|
|||
* @returns {Number} Returns the account balance
|
||||
*/
|
||||
async BalanceOf(ctx, owner) {
|
||||
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const balanceKey = ctx.stub.createCompositeKey(balancePrefix, [owner]);
|
||||
|
||||
const balanceBytes = await ctx.stub.getState(balanceKey);
|
||||
|
|
@ -99,6 +120,10 @@ class TokenERC20Contract extends Contract {
|
|||
* @returns {Boolean} Return whether the transfer was successful or not
|
||||
*/
|
||||
async Transfer(ctx, to, value) {
|
||||
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const from = ctx.clientIdentity.getID();
|
||||
|
||||
const transferResp = await this._transfer(ctx, from, to, value);
|
||||
|
|
@ -123,6 +148,10 @@ class TokenERC20Contract extends Contract {
|
|||
* @returns {Boolean} Return whether the transfer was successful or not
|
||||
*/
|
||||
async TransferFrom(ctx, from, to, value) {
|
||||
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const spender = ctx.clientIdentity.getID();
|
||||
|
||||
// Retrieve the allowance of the spender
|
||||
|
|
@ -149,7 +178,7 @@ class TokenERC20Contract extends Contract {
|
|||
}
|
||||
|
||||
// Decrease the allowance
|
||||
const updatedAllowance = currentAllowance - valueInt;
|
||||
const updatedAllowance = this.sub(currentAllowance, valueInt);
|
||||
await ctx.stub.putState(allowanceKey, Buffer.from(updatedAllowance.toString()));
|
||||
console.log(`spender ${spender} allowance updated from ${currentAllowance} to ${updatedAllowance}`);
|
||||
|
||||
|
|
@ -202,8 +231,8 @@ class TokenERC20Contract extends Contract {
|
|||
}
|
||||
|
||||
// Update the balance
|
||||
const fromUpdatedBalance = fromCurrentBalance - valueInt;
|
||||
const toUpdatedBalance = toCurrentBalance + valueInt;
|
||||
const fromUpdatedBalance = this.sub(fromCurrentBalance, valueInt);
|
||||
const toUpdatedBalance = this.add(toCurrentBalance, valueInt);
|
||||
|
||||
await ctx.stub.putState(fromBalanceKey, Buffer.from(fromUpdatedBalance.toString()));
|
||||
await ctx.stub.putState(toBalanceKey, Buffer.from(toUpdatedBalance.toString()));
|
||||
|
|
@ -223,6 +252,10 @@ class TokenERC20Contract extends Contract {
|
|||
* @returns {Boolean} Return whether the approval was successful or not
|
||||
*/
|
||||
async Approve(ctx, spender, value) {
|
||||
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const owner = ctx.clientIdentity.getID();
|
||||
|
||||
const allowanceKey = ctx.stub.createCompositeKey(allowancePrefix, [owner, spender]);
|
||||
|
|
@ -247,6 +280,10 @@ class TokenERC20Contract extends Contract {
|
|||
* @returns {Number} Return the amount of remaining tokens allowed to spent
|
||||
*/
|
||||
async Allowance(ctx, owner, spender) {
|
||||
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const allowanceKey = ctx.stub.createCompositeKey(allowancePrefix, [owner, spender]);
|
||||
|
||||
const allowanceBytes = await ctx.stub.getState(allowanceKey);
|
||||
|
|
@ -269,7 +306,19 @@ class TokenERC20Contract extends Contract {
|
|||
* @param {String} decimals The decimals of the token
|
||||
* @param {String} totalSupply The totalSupply of the token
|
||||
*/
|
||||
async SetOption(ctx, name, symbol, decimals) {
|
||||
async Initialize(ctx, name, symbol, decimals) {
|
||||
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to set Options for these tokens
|
||||
const clientMSPID = ctx.clientIdentity.getMSPID();
|
||||
if (clientMSPID !== 'Org1MSP') {
|
||||
throw new Error('client is not authorized to initialize contract');
|
||||
}
|
||||
|
||||
//check contract options are not already set, client is not authorized to change them once intitialized
|
||||
const nameBytes = await ctx.stub.getState(nameKey);
|
||||
if (nameBytes !== undefined) {
|
||||
throw new Error('contract options are already set, client is not authorized to change them');
|
||||
}
|
||||
|
||||
await ctx.stub.putState(nameKey, Buffer.from(name));
|
||||
await ctx.stub.putState(symbolKey, Buffer.from(symbol));
|
||||
await ctx.stub.putState(decimalsKey, Buffer.from(decimals));
|
||||
|
|
@ -287,6 +336,9 @@ class TokenERC20Contract extends Contract {
|
|||
*/
|
||||
async Mint(ctx, amount) {
|
||||
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to mint new tokens
|
||||
const clientMSPID = ctx.clientIdentity.getMSPID();
|
||||
if (clientMSPID !== 'Org1MSP') {
|
||||
|
|
@ -311,7 +363,7 @@ class TokenERC20Contract extends Contract {
|
|||
} else {
|
||||
currentBalance = parseInt(currentBalanceBytes.toString());
|
||||
}
|
||||
const updatedBalance = currentBalance + amountInt;
|
||||
const updatedBalance = this.add(currentBalance, amountInt);
|
||||
|
||||
await ctx.stub.putState(balanceKey, Buffer.from(updatedBalance.toString()));
|
||||
|
||||
|
|
@ -324,7 +376,7 @@ class TokenERC20Contract extends Contract {
|
|||
} else {
|
||||
totalSupply = parseInt(totalSupplyBytes.toString());
|
||||
}
|
||||
totalSupply = totalSupply + amountInt;
|
||||
totalSupply = this.add(totalSupply, amountInt);
|
||||
await ctx.stub.putState(totalSupplyKey, Buffer.from(totalSupply.toString()));
|
||||
|
||||
// Emit the Transfer event
|
||||
|
|
@ -344,6 +396,9 @@ class TokenERC20Contract extends Contract {
|
|||
*/
|
||||
async Burn(ctx, amount) {
|
||||
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to burn tokens
|
||||
const clientMSPID = ctx.clientIdentity.getMSPID();
|
||||
if (clientMSPID !== 'Org1MSP') {
|
||||
|
|
@ -361,7 +416,7 @@ class TokenERC20Contract extends Contract {
|
|||
throw new Error('The balance does not exist');
|
||||
}
|
||||
const currentBalance = parseInt(currentBalanceBytes.toString());
|
||||
const updatedBalance = currentBalance - amountInt;
|
||||
const updatedBalance = this.sub(currentBalance, amountInt);
|
||||
|
||||
await ctx.stub.putState(balanceKey, Buffer.from(updatedBalance.toString()));
|
||||
|
||||
|
|
@ -370,7 +425,7 @@ class TokenERC20Contract extends Contract {
|
|||
if (!totalSupplyBytes || totalSupplyBytes.length === 0) {
|
||||
throw new Error('totalSupply does not exist.');
|
||||
}
|
||||
const totalSupply = parseInt(totalSupplyBytes.toString()) - amountInt;
|
||||
const totalSupply = this.sub(parseInt(totalSupplyBytes.toString()), amountInt);
|
||||
await ctx.stub.putState(totalSupplyKey, Buffer.from(totalSupply.toString()));
|
||||
|
||||
// Emit the Transfer event
|
||||
|
|
@ -388,6 +443,10 @@ class TokenERC20Contract extends Contract {
|
|||
* @returns {Number} Returns the account balance
|
||||
*/
|
||||
async ClientAccountBalance(ctx) {
|
||||
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
// Get ID of submitting client identity
|
||||
const clientAccountID = ctx.clientIdentity.getID();
|
||||
|
||||
|
|
@ -405,11 +464,40 @@ class TokenERC20Contract extends Contract {
|
|||
// In this implementation, the client account ID is the clientId itself.
|
||||
// Users can use this function to get their own account id, which they can then give to others as the payment address
|
||||
async ClientAccountID(ctx) {
|
||||
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
// Get ID of submitting client identity
|
||||
const clientAccountID = ctx.clientIdentity.getID();
|
||||
return clientAccountID;
|
||||
}
|
||||
|
||||
//Checks that contract options have been already initialized
|
||||
async CheckInitialized(ctx){
|
||||
const nameBytes = await ctx.stub.getState(nameKey);
|
||||
if (nameBytes === undefined) {
|
||||
throw new Error('contract options need to be set before calling any function, call Initialize() to initialize contract');
|
||||
}
|
||||
}
|
||||
|
||||
// add two number checking for overflow
|
||||
add(a, b) {
|
||||
let c = a + b;
|
||||
if (a !== c - b || b !== c - a){
|
||||
throw new Error(`Math: addition overflow occurred ${a} + ${b}`);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
// add two number checking for overflow
|
||||
sub(a, b) {
|
||||
let c = a - b;
|
||||
if (a !== c + b || b !== a - c){
|
||||
throw new Error(`Math: subtraction overflow occurred ${a} - ${b}`);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TokenERC20Contract;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ describe('Chaincode', () => {
|
|||
let mockStub;
|
||||
let mockClientIdentity;
|
||||
|
||||
beforeEach('Sandbox creation', () => {
|
||||
beforeEach('Sandbox creation', async () => {
|
||||
sandbox = sinon.createSandbox();
|
||||
token = new TokenERC20Contract('token-erc20');
|
||||
|
||||
|
|
@ -36,6 +36,8 @@ describe('Chaincode', () => {
|
|||
mockClientIdentity = sinon.createStubInstance(ClientIdentity);
|
||||
ctx.clientIdentity = mockClientIdentity;
|
||||
|
||||
await token.Initialize(ctx, 'some name', 'some symbol', '2');
|
||||
|
||||
mockStub.putState.resolves('some state');
|
||||
mockStub.setEvent.returns('set event');
|
||||
|
||||
|
|
@ -198,13 +200,18 @@ describe('Chaincode', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('#SetOption', () => {
|
||||
describe('#Initialize', () => {
|
||||
it('should work', async () => {
|
||||
const response = await token.SetOption(ctx, 'some name', 'some symbol', '2');
|
||||
//we consider that is been already initialize in the before each statement
|
||||
sinon.assert.calledWith(mockStub.putState, 'name', Buffer.from('some name'));
|
||||
sinon.assert.calledWith(mockStub.putState, 'symbol', Buffer.from('some symbol'));
|
||||
sinon.assert.calledWith(mockStub.putState, 'decimals', Buffer.from('2'));
|
||||
expect(response).to.equals(true);
|
||||
});
|
||||
|
||||
it('should failed if called a second time', async () => {
|
||||
//we consider that is been already initialize in the before each statement
|
||||
await expect(await token.Initialize(ctx, 'some name', 'some symbol', '2'))
|
||||
.to.be.rejectedWith(Error, 'contract options are already set, client is not authorized to change them');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -60,6 +60,16 @@ func _nftExists(ctx contractapi.TransactionContextInterface, tokenId string) boo
|
|||
// param owner {String} An owner for whom to query the balance
|
||||
// returns {int} The number of non-fungible tokens owned by the owner, possibly zero
|
||||
func (c *TokenERC721Contract) BalanceOf(ctx contractapi.TransactionContextInterface, owner string) int {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
panic("failed to check if contract ia already initialized:"+ err.Error())
|
||||
}
|
||||
if !initialized {
|
||||
panic("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// There is a key record for every non-fungible token in the format of balancePrefix.owner.tokenId.
|
||||
// BalanceOf() queries for and counts all records matching balancePrefix.owner.*
|
||||
|
||||
|
|
@ -85,6 +95,16 @@ func (c *TokenERC721Contract) BalanceOf(ctx contractapi.TransactionContextInterf
|
|||
// param {String} tokenId The identifier for a non-fungible token
|
||||
// returns {String} Return the owner of the non-fungible token
|
||||
func (c *TokenERC721Contract) OwnerOf(ctx contractapi.TransactionContextInterface, tokenId string) (string, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "", fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
nft, err := _readNFT(ctx, tokenId)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not process OwnerOf for tokenId: %w", err)
|
||||
|
|
@ -98,6 +118,16 @@ func (c *TokenERC721Contract) OwnerOf(ctx contractapi.TransactionContextInterfac
|
|||
// param {String} tokenId the non-fungible token to approve
|
||||
// returns {Boolean} Return whether the approval was successful or not
|
||||
func (c *TokenERC721Contract) Approve(ctx contractapi.TransactionContextInterface, operator string, tokenId string) (bool, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return false, fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
sender64, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to GetClientIdentity: %v", err)
|
||||
|
|
@ -151,6 +181,16 @@ func (c *TokenERC721Contract) Approve(ctx contractapi.TransactionContextInterfac
|
|||
// param {Boolean} approved True if the operator is approved, false to revoke approval
|
||||
// returns {Boolean} Return whether the approval was successful or not
|
||||
func (c *TokenERC721Contract) SetApprovalForAll(ctx contractapi.TransactionContextInterface, operator string, approved bool) (bool, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return false, fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
sender64, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to GetClientIdentity: %v", err)
|
||||
|
|
@ -196,6 +236,16 @@ func (c *TokenERC721Contract) SetApprovalForAll(ctx contractapi.TransactionConte
|
|||
// param {String} operator The client that acts on behalf of the owner
|
||||
// returns {Boolean} Return true if the operator is an approved operator for the owner, false otherwise
|
||||
func (c *TokenERC721Contract) IsApprovedForAll(ctx contractapi.TransactionContextInterface, owner string, operator string) (bool, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return false,fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return false, fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
approvalKey, err := ctx.GetStub().CreateCompositeKey(approvalPrefix, []string{owner, operator})
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to CreateCompositeKey: %v", err)
|
||||
|
|
@ -223,6 +273,16 @@ func (c *TokenERC721Contract) IsApprovedForAll(ctx contractapi.TransactionContex
|
|||
// param {String} tokenId the non-fungible token to find the approved client for
|
||||
// returns {Object} Return the approved client for this non-fungible token, or null if there is none
|
||||
func (c *TokenERC721Contract) GetApproved(ctx contractapi.TransactionContextInterface, tokenId string) (string, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "false",fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "false", fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
nft, err := _readNFT(ctx, tokenId)
|
||||
if err != nil {
|
||||
return "false", fmt.Errorf("failed GetApproved for tokenId : %v", err)
|
||||
|
|
@ -239,6 +299,16 @@ func (c *TokenERC721Contract) GetApproved(ctx contractapi.TransactionContextInte
|
|||
|
||||
func (c *TokenERC721Contract) TransferFrom(ctx contractapi.TransactionContextInterface, from string, to string, tokenId string) (bool, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return false, fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
|
||||
// Get ID of submitting client identity
|
||||
sender64, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
|
|
@ -336,6 +406,16 @@ func (c *TokenERC721Contract) TransferFrom(ctx contractapi.TransactionContextInt
|
|||
// returns {String} Returns the name of the token
|
||||
|
||||
func (c *TokenERC721Contract) Name(ctx contractapi.TransactionContextInterface) (string, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "", fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
bytes, err := ctx.GetStub().GetState(nameKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get Name bytes: %s", err)
|
||||
|
|
@ -348,6 +428,16 @@ func (c *TokenERC721Contract) Name(ctx contractapi.TransactionContextInterface)
|
|||
// returns {String} Returns the symbol of the token
|
||||
|
||||
func (c *TokenERC721Contract) Symbol(ctx contractapi.TransactionContextInterface) (string, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "", fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
bytes, err := ctx.GetStub().GetState(symbolKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get Symbol: %v", err)
|
||||
|
|
@ -361,6 +451,16 @@ func (c *TokenERC721Contract) Symbol(ctx contractapi.TransactionContextInterface
|
|||
// returns {String} Returns the URI of the token
|
||||
|
||||
func (c *TokenERC721Contract) TokenURI(ctx contractapi.TransactionContextInterface, tokenId string) (string, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "", fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
nft, err := _readNFT(ctx, tokenId)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get TokenURI: %v", err)
|
||||
|
|
@ -376,6 +476,16 @@ func (c *TokenERC721Contract) TokenURI(ctx contractapi.TransactionContextInterfa
|
|||
// where each one of them has an assigned and queryable owner.
|
||||
|
||||
func (c *TokenERC721Contract) TotalSupply(ctx contractapi.TransactionContextInterface) int {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
panic("failed to check if contract ia already initialized:"+ err.Error())
|
||||
}
|
||||
if !initialized {
|
||||
panic("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// There is a key record for every non-fungible token in the format of nftPrefix.tokenId.
|
||||
// TotalSupply() queries for and counts all records matching nftPrefix.*
|
||||
|
||||
|
|
@ -399,11 +509,11 @@ func (c *TokenERC721Contract) TotalSupply(ctx contractapi.TransactionContextInte
|
|||
}
|
||||
|
||||
// ============== ERC721 enumeration extension ===============
|
||||
// Set optional information for a token.
|
||||
// Set information for a token and intialize contract.
|
||||
// param {String} name The name of the token
|
||||
// param {String} symbol The symbol of the token
|
||||
|
||||
func (c *TokenERC721Contract) SetOption(ctx contractapi.TransactionContextInterface, name string, symbol string) (bool, error) {
|
||||
func (c *TokenERC721Contract) Initialize(ctx contractapi.TransactionContextInterface, name string, symbol string) (bool, error) {
|
||||
// Check minter authorization - this sample assumes Org1 is the issuer with privilege to set the name and symbol
|
||||
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
||||
if err != nil {
|
||||
|
|
@ -412,6 +522,13 @@ func (c *TokenERC721Contract) SetOption(ctx contractapi.TransactionContextInterf
|
|||
return false, fmt.Errorf("client is not authorized to set the name and symbol of the token")
|
||||
}
|
||||
|
||||
bytes, err := ctx.GetStub().GetState(nameKey)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get Name: %v", err)
|
||||
}else if bytes != nil {
|
||||
return false, fmt.Errorf("contract options are already set, client is not authorized to change them")
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(nameKey, []byte(name))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to PutState nameKey %s: %v", nameKey, err)
|
||||
|
|
@ -432,6 +549,16 @@ func (c *TokenERC721Contract) SetOption(ctx contractapi.TransactionContextInterf
|
|||
|
||||
func (c *TokenERC721Contract) MintWithTokenURI(ctx contractapi.TransactionContextInterface, tokenId string, tokenURI string) (*Nft, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return nil, fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the issuer with privilege to mint a new token
|
||||
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
||||
if err != nil {
|
||||
|
|
@ -518,6 +645,16 @@ func (c *TokenERC721Contract) MintWithTokenURI(ctx contractapi.TransactionContex
|
|||
// param {String} tokenId Unique ID of a non-fungible token
|
||||
// returns {Boolean} Return whether the burn was successful or not
|
||||
func (c *TokenERC721Contract) Burn(ctx contractapi.TransactionContextInterface, tokenId string) (bool, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return false, fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
owner64, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to GetClientIdentity owner64: %v", err)
|
||||
|
|
@ -582,6 +719,16 @@ func (c *TokenERC721Contract) Burn(ctx contractapi.TransactionContextInterface,
|
|||
// ClientAccountBalance returns the balance of the requesting client's account.
|
||||
// returns {Number} Returns the account balance
|
||||
func (c *TokenERC721Contract) ClientAccountBalance(ctx contractapi.TransactionContextInterface) (int, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return 0, fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Get ID of submitting client identity
|
||||
clientAccountID64, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
|
|
@ -603,6 +750,16 @@ func (c *TokenERC721Contract) ClientAccountBalance(ctx contractapi.TransactionCo
|
|||
// Users can use this function to get their own account id, which they can then give to others as the payment address
|
||||
|
||||
func (c *TokenERC721Contract) ClientAccountID(ctx contractapi.TransactionContextInterface) (string, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "", fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Get ID of submitting client identity
|
||||
clientAccountID64, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
|
|
@ -617,3 +774,14 @@ func (c *TokenERC721Contract) ClientAccountID(ctx contractapi.TransactionContext
|
|||
|
||||
return clientAccount, nil
|
||||
}
|
||||
|
||||
//Checks that contract options have been already initialized
|
||||
func checkInitialized(ctx contractapi.TransactionContextInterface) (bool, error) {
|
||||
tokenName, err := ctx.GetStub().GetState(nameKey)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get token name: %v", err)
|
||||
}else if (tokenName == nil){
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -242,11 +242,11 @@ func TestTokenURI(t *testing.T) {
|
|||
assert.Equal(t, "https://example.com/nft101.json", tokenURI)
|
||||
}
|
||||
|
||||
func TestSetOption(t *testing.T) {
|
||||
func TestInitialize(t *testing.T) {
|
||||
ctx, _ := setupStub()
|
||||
c := new(TokenERC721Contract)
|
||||
|
||||
option, _ := c.SetOption(ctx, "someName", "someSymbol")
|
||||
option, _ := c.Initialize(ctx, "someName", "someSymbol")
|
||||
assert.Equal(t, true, option)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ public class ERC721TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public long BalanceOf(final Context ctx, final String owner) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
final ChaincodeStub stub = ctx.getStub();
|
||||
final CompositeKey balanceKey =
|
||||
stub.createCompositeKey(ContractConstants.BALANCE.getValue(), owner);
|
||||
|
|
@ -76,6 +78,8 @@ public class ERC721TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public String OwnerOf(final Context ctx, final String tokenId) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
final NFT nft = this._readNft(ctx, tokenId);
|
||||
if (stringIsNullOrEmpty(nft.getOwner())) {
|
||||
final String errorMessage = String.format("No owner is assigned o the token %s", tokenId);
|
||||
|
|
@ -94,6 +98,8 @@ public class ERC721TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public boolean IsApprovedForAll(final Context ctx, final String owner, final String operator) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
final ChaincodeStub stub = ctx.getStub();
|
||||
final CompositeKey approvalKey =
|
||||
stub.createCompositeKey(ContractConstants.APPROVAL.getValue(), owner, operator);
|
||||
|
|
@ -116,6 +122,8 @@ public class ERC721TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||
public void Approve(final Context ctx, final String operator, final String tokenId) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
final ChaincodeStub stub = ctx.getStub();
|
||||
final String sender = ctx.getClientIdentity().getId();
|
||||
NFT nft = this._readNft(ctx, tokenId);
|
||||
|
|
@ -144,6 +152,8 @@ public class ERC721TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||
public void SetApprovalForAll(final Context ctx, final String operator, final boolean approved) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
final String sender = ctx.getClientIdentity().getId();
|
||||
final ChaincodeStub stub = ctx.getStub();
|
||||
final Approval nftApproval = new Approval(sender, operator, approved);
|
||||
|
|
@ -163,6 +173,8 @@ public class ERC721TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public String GetApproved(final Context ctx, final String tokenId) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
final NFT nft = this._readNft(ctx, tokenId);
|
||||
return nft.getApproved();
|
||||
}
|
||||
|
|
@ -178,6 +190,8 @@ public class ERC721TokenContract implements ContractInterface {
|
|||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||
public void TransferFrom(
|
||||
final Context ctx, final String from, final String to, final String tokenId) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
final String sender = ctx.getClientIdentity().getId();
|
||||
final ChaincodeStub stub = ctx.getStub();
|
||||
NFT nft = this._readNft(ctx, tokenId);
|
||||
|
|
@ -235,6 +249,8 @@ public class ERC721TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public String Name(final Context ctx) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
return ctx.getStub().getStringState(ContractConstants.NAMEKEY.getValue());
|
||||
}
|
||||
|
||||
|
|
@ -246,6 +262,8 @@ public class ERC721TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public String Symbol(final Context ctx) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
return ctx.getStub().getStringState(ContractConstants.SYMBOLKEY.getValue());
|
||||
}
|
||||
|
||||
|
|
@ -258,6 +276,8 @@ public class ERC721TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public String TokenURI(final Context ctx, final String tokenId) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
final NFT nft = this._readNft(ctx, tokenId);
|
||||
return nft.getTokenURI();
|
||||
}
|
||||
|
|
@ -275,6 +295,8 @@ public class ERC721TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public long TotalSupply(final Context ctx) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
final ChaincodeStub stub = ctx.getStub();
|
||||
final CompositeKey nftKey = stub.createCompositeKey(ContractConstants.NFT.getValue());
|
||||
final QueryResultsIterator<KeyValue> iterator = stub.getStateByPartialCompositeKey(nftKey);
|
||||
|
|
@ -297,16 +319,24 @@ public class ERC721TokenContract implements ContractInterface {
|
|||
* @param symbol The symbol of the token
|
||||
*/
|
||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||
public void SetOption(final Context ctx, final String name, final String symbol) {
|
||||
public void Initialize(final Context ctx, final String name, final String symbol) {
|
||||
|
||||
final ChaincodeStub stub = ctx.getStub();
|
||||
|
||||
final String clientMSPID = ctx.getClientIdentity().getMSPID();
|
||||
// Check minter authorization - this sample assumes Org1 is the issuer with privilege to set the
|
||||
// name and symbol
|
||||
if (!clientMSPID.equalsIgnoreCase(ContractConstants.MINTER_ORG_MSP.getValue())) {
|
||||
throw new ChaincodeException(
|
||||
"Client is not authorized to set the name and symbol of the token");
|
||||
"Client is not authorized to initialize the contract (set the name and symbol of the token)");
|
||||
}
|
||||
final ChaincodeStub stub = ctx.getStub();
|
||||
|
||||
//check contract options are not already set, client is not authorized to change them once intitialized
|
||||
String tokenName = stub.getStringState(ContractConstants.NAMEKEY.getValue());
|
||||
if (!stringIsNullOrEmpty(tokenName)) {
|
||||
throw new ChaincodeException("contract options are already set, client is not authorized to change them");
|
||||
}
|
||||
|
||||
stub.putStringState(ContractConstants.NAMEKEY.getValue(), name);
|
||||
stub.putStringState(ContractConstants.SYMBOLKEY.getValue(), symbol);
|
||||
}
|
||||
|
|
@ -321,6 +351,8 @@ public class ERC721TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||
public NFT MintWithTokenURI(final Context ctx, final String tokenId, final String tokenURI) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
final String clientMSPID = ctx.getClientIdentity().getMSPID();
|
||||
final ChaincodeStub stub = ctx.getStub();
|
||||
// Check minter authorization this sample assumes Org1 is the issuer with privilege to mint a
|
||||
|
|
@ -360,6 +392,8 @@ public class ERC721TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||
public void Burn(final Context ctx, final String tokenId) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
final ChaincodeStub stub = ctx.getStub();
|
||||
final String owner = ctx.getClientIdentity().getId();
|
||||
// Check if a caller is the owner of the non-fungible token
|
||||
|
|
@ -390,6 +424,8 @@ public class ERC721TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public long ClientAccountBalance(final Context ctx) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
return this.BalanceOf(ctx, ctx.getClientIdentity().getId());
|
||||
}
|
||||
|
||||
|
|
@ -403,6 +439,8 @@ public class ERC721TokenContract implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public String ClientAccountID(final Context ctx) {
|
||||
//check contract options are already set first to execute the function
|
||||
this.checkInitialized(ctx);
|
||||
return ctx.getClientIdentity().getId();
|
||||
}
|
||||
|
||||
|
|
@ -437,4 +475,17 @@ public class ERC721TokenContract implements ContractInterface {
|
|||
final String nft = stub.getStringState(nftKey.toString());
|
||||
return ((stringIsNullOrEmpty(nft)) ? false : true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that contract options have been already initialized
|
||||
*
|
||||
* @param ctx the transaction context
|
||||
* @return the number of decimals
|
||||
*/
|
||||
private void checkInitialized(final Context ctx) {
|
||||
String tokenName = ctx.getStub().getStringState(ContractConstants.NAMEKEY.getValue());
|
||||
if (stringIsNullOrEmpty(tokenName)) {
|
||||
throw new ChaincodeException("Contract options need to be set before calling any function, call Initialize() to initialize contract", ContractErrors.TOKEN_NOT_FOUND.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ public class ERC721TokenContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
List<KeyValue> list = new ArrayList<>();
|
||||
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||
list.add(new MockKeyValue("balance_Alice_101", "\u0000"));
|
||||
list.add(new MockKeyValue("balance_Alice_101", "\u0000"));
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
|
|
@ -103,6 +104,7 @@ public class ERC721TokenContractTest {
|
|||
NFT nft = new NFT("101", "Alicd", "http://test.com", "");
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
CompositeKey ck = mock(CompositeKey.class);
|
||||
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||
when(ck.toString()).thenReturn(ContractConstants.NFT.getValue() + "101");
|
||||
when(stub.createCompositeKey(ContractConstants.NFT.getValue(), "101")).thenReturn(ck);
|
||||
when(stub.getStringState(ck.toString())).thenReturn(nft.toJSONString());
|
||||
|
|
@ -129,6 +131,7 @@ public class ERC721TokenContractTest {
|
|||
this.stub = mock(ChaincodeStub.class);
|
||||
when(this.ctx.getStub()).thenReturn(stub);
|
||||
contract = new ERC721TokenContract();
|
||||
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||
CompositeKey ck1 = mock(CompositeKey.class);
|
||||
when(ck1.toString()).thenReturn(ContractConstants.NFT.getValue() + "101");
|
||||
when(this.stub.createCompositeKey(ContractConstants.NFT.getValue(), "101")).thenReturn(ck1);
|
||||
|
|
@ -256,6 +259,7 @@ public class ERC721TokenContractTest {
|
|||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
NFT nft = new NFT("101", "Alice", "http://test.com", "");
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||
CompositeKey ck = mock(CompositeKey.class);
|
||||
when(ck.toString()).thenReturn(ContractConstants.NFT.getValue() + "101");
|
||||
when(stub.createCompositeKey(ContractConstants.NFT.getValue(), "101")).thenReturn(ck);
|
||||
|
|
@ -281,6 +285,7 @@ public class ERC721TokenContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||
ClientIdentity ci = null;
|
||||
ci = mock(ClientIdentity.class);
|
||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||
|
|
@ -306,6 +311,7 @@ public class ERC721TokenContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||
ClientIdentity ci = null;
|
||||
ci = mock(ClientIdentity.class);
|
||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||
|
|
@ -327,6 +333,7 @@ public class ERC721TokenContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||
ClientIdentity ci = null;
|
||||
ci = mock(ClientIdentity.class);
|
||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||
|
|
@ -362,6 +369,7 @@ public class ERC721TokenContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ANFT");
|
||||
ERC721TokenContract contract = new ERC721TokenContract();
|
||||
final String name = contract.Name(ctx);
|
||||
|
|
@ -373,6 +381,7 @@ public class ERC721TokenContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||
ERC721TokenContract contract = new ERC721TokenContract();
|
||||
final NFT nft = new NFT("101", "Alice", "http://test.com", "Bob");
|
||||
CompositeKey ck = mock(CompositeKey.class);
|
||||
|
|
@ -388,6 +397,7 @@ public class ERC721TokenContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||
List<KeyValue> list = new ArrayList<>();
|
||||
list.add(
|
||||
new MockKeyValue(
|
||||
|
|
@ -417,6 +427,7 @@ public class ERC721TokenContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||
final NFT nft = new NFT("101", "Alice", "DummyURI", "");
|
||||
CompositeKey ck = mock(CompositeKey.class);
|
||||
when(ck.toString()).thenReturn(ContractConstants.NFT.getValue() + "101");
|
||||
|
|
@ -445,6 +456,7 @@ public class ERC721TokenContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||
final NFT nft = new NFT("101", "Alice", "DummyURI", "");
|
||||
CompositeKey ck = mock(CompositeKey.class);
|
||||
when(ck.toString()).thenReturn(ContractConstants.NFT.getValue() + "101");
|
||||
|
|
@ -471,6 +483,7 @@ public class ERC721TokenContractTest {
|
|||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
when(ctx.getStub()).thenReturn(stub);
|
||||
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||
final NFT nft = new NFT("101", "Alice", "DummyURI", "");
|
||||
CompositeKey ck = mock(CompositeKey.class);
|
||||
when(ck.toString()).thenReturn(ContractConstants.NFT.getValue() + "101");
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ class TokenERC721Contract extends Contract {
|
|||
* @returns {Number} The number of non-fungible tokens owned by the owner, possibly zero
|
||||
*/
|
||||
async BalanceOf(ctx, owner) {
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
// There is a key record for every non-fungible token in the format of balancePrefix.owner.tokenId.
|
||||
// BalanceOf() queries for and counts all records matching balancePrefix.owner.*
|
||||
const iterator = await ctx.stub.getStateByPartialCompositeKey(balancePrefix, [owner]);
|
||||
|
|
@ -47,6 +50,9 @@ class TokenERC721Contract extends Contract {
|
|||
* @returns {String} Return the owner of the non-fungible token
|
||||
*/
|
||||
async OwnerOf(ctx, tokenId) {
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const nft = await this._readNFT(ctx, tokenId);
|
||||
const owner = nft.owner;
|
||||
if (!owner) {
|
||||
|
|
@ -67,6 +73,9 @@ class TokenERC721Contract extends Contract {
|
|||
* @returns {Boolean} Return whether the transfer was successful or not
|
||||
*/
|
||||
async TransferFrom(ctx, from, to, tokenId) {
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const sender = ctx.clientIdentity.getID();
|
||||
|
||||
const nft = await this._readNFT(ctx, tokenId);
|
||||
|
|
@ -118,6 +127,9 @@ class TokenERC721Contract extends Contract {
|
|||
* @returns {Boolean} Return whether the approval was successful or not
|
||||
*/
|
||||
async Approve(ctx, approved, tokenId) {
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const sender = ctx.clientIdentity.getID();
|
||||
|
||||
const nft = await this._readNFT(ctx, tokenId);
|
||||
|
|
@ -153,6 +165,9 @@ class TokenERC721Contract extends Contract {
|
|||
* @returns {Boolean} Return whether the approval was successful or not
|
||||
*/
|
||||
async SetApprovalForAll(ctx, operator, approved) {
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const sender = ctx.clientIdentity.getID();
|
||||
|
||||
const approval = { owner: sender, operator: operator, approved: approved };
|
||||
|
|
@ -174,6 +189,9 @@ class TokenERC721Contract extends Contract {
|
|||
* @returns {Object} Return the approved client for this non-fungible token, or null if there is none
|
||||
*/
|
||||
async GetApproved(ctx, tokenId) {
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const nft = await this._readNFT(ctx, tokenId);
|
||||
return nft.approved;
|
||||
}
|
||||
|
|
@ -187,6 +205,9 @@ class TokenERC721Contract extends Contract {
|
|||
* @returns {Boolean} Return true if the operator is an approved operator for the owner, false otherwise
|
||||
*/
|
||||
async IsApprovedForAll(ctx, owner, operator) {
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const approvalKey = ctx.stub.createCompositeKey(approvalPrefix, [owner, operator]);
|
||||
const approvalBytes = await ctx.stub.getState(approvalKey);
|
||||
let approved;
|
||||
|
|
@ -209,6 +230,9 @@ class TokenERC721Contract extends Contract {
|
|||
* @returns {String} Returns the name of the token
|
||||
*/
|
||||
async Name(ctx) {
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const nameAsBytes = await ctx.stub.getState(nameKey);
|
||||
return nameAsBytes.toString();
|
||||
}
|
||||
|
|
@ -220,6 +244,9 @@ class TokenERC721Contract extends Contract {
|
|||
* @returns {String} Returns the symbol of the token
|
||||
*/
|
||||
async Symbol(ctx) {
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const symbolAsBytes = await ctx.stub.getState(symbolKey);
|
||||
return symbolAsBytes.toString();
|
||||
}
|
||||
|
|
@ -232,6 +259,9 @@ class TokenERC721Contract extends Contract {
|
|||
* @returns {String} Returns the URI of the token
|
||||
*/
|
||||
async TokenURI(ctx, tokenId) {
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const nft = await this._readNFT(ctx, tokenId);
|
||||
return nft.tokenURI;
|
||||
}
|
||||
|
|
@ -246,6 +276,9 @@ class TokenERC721Contract extends Contract {
|
|||
* where each one of them has an assigned and queryable owner.
|
||||
*/
|
||||
async TotalSupply(ctx) {
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
// There is a key record for every non-fungible token in the format of nftPrefix.tokenId.
|
||||
// TotalSupply() queries for and counts all records matching nftPrefix.*
|
||||
const iterator = await ctx.stub.getStateByPartialCompositeKey(nftPrefix, []);
|
||||
|
|
@ -269,14 +302,20 @@ class TokenERC721Contract extends Contract {
|
|||
* @param {String} name The name of the token
|
||||
* @param {String} symbol The symbol of the token
|
||||
*/
|
||||
async SetOption(ctx, name, symbol) {
|
||||
async Initialize(ctx, name, symbol) {
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the issuer with privilege to set the name and symbol
|
||||
// Check minter authorization - this sample assumes Org1 is the issuer with privilege to initialize contract (set the name and symbol)
|
||||
const clientMSPID = ctx.clientIdentity.getMSPID();
|
||||
if (clientMSPID !== 'Org1MSP') {
|
||||
throw new Error('client is not authorized to set the name and symbol of the token');
|
||||
}
|
||||
|
||||
//check contract options are not already set, client is not authorized to change them once intitialized
|
||||
const nameBytes = await ctx.stub.getState(nameKey);
|
||||
if (nameBytes !== undefined) {
|
||||
throw new Error('contract options are already set, client is not authorized to change them');
|
||||
}
|
||||
|
||||
await ctx.stub.putState(nameKey, Buffer.from(name));
|
||||
await ctx.stub.putState(symbolKey, Buffer.from(symbol));
|
||||
return true;
|
||||
|
|
@ -291,6 +330,8 @@ class TokenERC721Contract extends Contract {
|
|||
* @returns {Object} Return the non-fungible token object
|
||||
*/
|
||||
async MintWithTokenURI(ctx, tokenId, tokenURI) {
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the issuer with privilege to mint a new token
|
||||
const clientMSPID = ctx.clientIdentity.getMSPID();
|
||||
|
|
@ -341,6 +382,9 @@ class TokenERC721Contract extends Contract {
|
|||
* @returns {Boolean} Return whether the burn was successful or not
|
||||
*/
|
||||
async Burn(ctx, tokenId) {
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
const owner = ctx.clientIdentity.getID();
|
||||
|
||||
// Check if a caller is the owner of the non-fungible token
|
||||
|
|
@ -388,6 +432,9 @@ class TokenERC721Contract extends Contract {
|
|||
* @returns {Number} Returns the account balance
|
||||
*/
|
||||
async ClientAccountBalance(ctx) {
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
// Get ID of submitting client identity
|
||||
const clientAccountID = ctx.clientIdentity.getID();
|
||||
return this.BalanceOf(ctx, clientAccountID);
|
||||
|
|
@ -397,10 +444,21 @@ class TokenERC721Contract extends Contract {
|
|||
// In this implementation, the client account ID is the clientId itself.
|
||||
// Users can use this function to get their own account id, which they can then give to others as the payment address
|
||||
async ClientAccountID(ctx) {
|
||||
//check contract options are already set first to execute the function
|
||||
await this.CheckInitialized(ctx);
|
||||
|
||||
// Get ID of submitting client identity
|
||||
const clientAccountID = ctx.clientIdentity.getID();
|
||||
return clientAccountID;
|
||||
}
|
||||
|
||||
//Checks that contract options have been already initialized
|
||||
async CheckIntitialized(ctx){
|
||||
const nameBytes = await ctx.stub.getState(nameKey);
|
||||
if (nameBytes === undefined) {
|
||||
throw new Error('contract options need to be set before calling any function, call Initialize() to initialize contract');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TokenERC721Contract;
|
||||
|
|
@ -20,9 +20,23 @@ type UTXO struct {
|
|||
Amount int `json:"amount"`
|
||||
}
|
||||
|
||||
// Define key names for options
|
||||
const nameKey = "name"
|
||||
const symbolKey = "symbol"
|
||||
const totalSupplyKey = "totalSupply"
|
||||
|
||||
// Mint creates a new unspent transaction output (UTXO) owned by the minter
|
||||
func (s *SmartContract) Mint(ctx contractapi.TransactionContextInterface, amount int) (*UTXO, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return nil, fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to mint new tokens
|
||||
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
||||
if err != nil {
|
||||
|
|
@ -63,6 +77,15 @@ func (s *SmartContract) Mint(ctx contractapi.TransactionContextInterface, amount
|
|||
// Transfer transfers UTXOs containing tokens from client to recipient(s)
|
||||
func (s *SmartContract) Transfer(ctx contractapi.TransactionContextInterface, utxoInputKeys []string, utxoOutputs []UTXO) ([]UTXO, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return nil, fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Get ID of submitting client identity
|
||||
clientID, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
|
|
@ -100,7 +123,7 @@ func (s *SmartContract) Transfer(ctx contractapi.TransactionContextInterface, ut
|
|||
Amount: amount,
|
||||
}
|
||||
|
||||
totalInputAmount += amount
|
||||
totalInputAmount,err = add(totalInputAmount, amount)
|
||||
utxoInputs[utxoInputKey] = utxoInput
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +138,7 @@ func (s *SmartContract) Transfer(ctx contractapi.TransactionContextInterface, ut
|
|||
|
||||
utxoOutputs[i].Key = fmt.Sprintf("%s.%d", txID, i)
|
||||
|
||||
totalOutputAmount += utxoOutput.Amount
|
||||
totalOutputAmount,err = add(totalOutputAmount, utxoOutput.Amount)
|
||||
}
|
||||
|
||||
// Validate total inputs equals total outputs
|
||||
|
|
@ -158,6 +181,15 @@ func (s *SmartContract) Transfer(ctx contractapi.TransactionContextInterface, ut
|
|||
// ClientUTXOs returns all UTXOs owned by the calling client
|
||||
func (s *SmartContract) ClientUTXOs(ctx contractapi.TransactionContextInterface) ([]*UTXO, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return nil, fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Get ID of submitting client identity
|
||||
clientID, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
|
|
@ -211,6 +243,15 @@ func (s *SmartContract) ClientUTXOs(ctx contractapi.TransactionContextInterface)
|
|||
// Users can use this function to get their own client id, which they can then give to others as the payment address
|
||||
func (s *SmartContract) ClientID(ctx contractapi.TransactionContextInterface) (string, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "", fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Get ID of submitting client identity
|
||||
clientID, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
|
|
@ -219,3 +260,109 @@ func (s *SmartContract) ClientID(ctx contractapi.TransactionContextInterface) (s
|
|||
|
||||
return clientID, nil
|
||||
}
|
||||
|
||||
// Name returns a descriptive name for fungible tokens in this contract
|
||||
// returns {String} Returns the name of the token
|
||||
|
||||
func (s *SmartContract) Name(ctx contractapi.TransactionContextInterface) (string, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "", fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
bytes, err := ctx.GetStub().GetState(nameKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get Name bytes: %s", err)
|
||||
}
|
||||
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
// Symbol returns an abbreviated name for fungible tokens in this contract.
|
||||
// returns {String} Returns the symbol of the token
|
||||
|
||||
func (s *SmartContract) Symbol(ctx contractapi.TransactionContextInterface) (string, error) {
|
||||
|
||||
//check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check if contract ia already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "", fmt.Errorf("Contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
bytes, err := ctx.GetStub().GetState(symbolKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get Symbol: %v", err)
|
||||
}
|
||||
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
// Set information for a token and intialize contract.
|
||||
// param {String} name The name of the token
|
||||
// param {String} symbol The symbol of the token
|
||||
func (s *SmartContract) Initialize(ctx contractapi.TransactionContextInterface, name string, symbol string) (bool, error) {
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to intitialize contract
|
||||
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get MSPID: %v", err)
|
||||
} else if clientMSPID != "Org1MSP" {
|
||||
return false, fmt.Errorf("client is not authorized to initialize contract")
|
||||
}
|
||||
|
||||
//check contract options are not already set, client is not authorized to change them once intitialized
|
||||
bytes, err := ctx.GetStub().GetState(nameKey)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get Name: %v", err)
|
||||
}else if bytes != nil {
|
||||
return false, fmt.Errorf("contract options are already set, client is not authorized to change them")
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(nameKey, []byte(name))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to set token name: %v", err)
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(symbolKey, []byte(symbol))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to set symbol: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("name: %v, symbol: %v", name, symbol)
|
||||
|
||||
return true, nil;
|
||||
}
|
||||
|
||||
//Checks that contract options have been already initialized
|
||||
func checkInitialized(ctx contractapi.TransactionContextInterface) (bool, error) {
|
||||
tokenName, err := ctx.GetStub().GetState(nameKey)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get token name: %v", err)
|
||||
}else if (tokenName == nil){
|
||||
return false , nil
|
||||
}
|
||||
return true , nil
|
||||
}
|
||||
|
||||
|
||||
// add two number checking for overflow
|
||||
func add(b int, q int) (int, error) {
|
||||
|
||||
// Check overflow
|
||||
var sum int
|
||||
sum = q + b
|
||||
|
||||
if (sum < q) == (b > 0 && q > 0) {
|
||||
return 0, fmt.Errorf("Math: addition overflow occurred %d + %d", b, q)
|
||||
}
|
||||
|
||||
return sum, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue