mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 15:35:09 +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"
|
const minterMSPID = "Org1MSP"
|
||||||
|
|
||||||
|
// Define key names for options
|
||||||
|
const nameKey = "name"
|
||||||
|
const symbolKey = "symbol"
|
||||||
|
|
||||||
// SmartContract provides functions for transferring tokens between accounts
|
// SmartContract provides functions for transferring tokens between accounts
|
||||||
type SmartContract struct {
|
type SmartContract struct {
|
||||||
contractapi.Contract
|
contractapi.Contract
|
||||||
|
|
@ -113,8 +117,17 @@ type ToID struct {
|
||||||
// This function emits a TransferSingle event.
|
// This function emits a TransferSingle event.
|
||||||
func (s *SmartContract) Mint(ctx contractapi.TransactionContextInterface, account string, id uint64, amount uint64) error {
|
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
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -140,12 +153,21 @@ func (s *SmartContract) Mint(ctx contractapi.TransactionContextInterface, accoun
|
||||||
// This function emits a TransferBatch event.
|
// This function emits a TransferBatch event.
|
||||||
func (s *SmartContract) MintBatch(ctx contractapi.TransactionContextInterface, account string, ids []uint64, amounts []uint64) error {
|
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) {
|
if len(ids) != len(amounts) {
|
||||||
return fmt.Errorf("ids and amounts must have the same length")
|
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
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -160,7 +182,10 @@ func (s *SmartContract) MintBatch(ctx contractapi.TransactionContextInterface, a
|
||||||
amountToSend := make(map[uint64]uint64) // token id => amount
|
amountToSend := make(map[uint64]uint64) // token id => amount
|
||||||
|
|
||||||
for i := 0; i < len(amounts); i++ {
|
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
|
// 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.
|
// This function triggers a TransferSingle event.
|
||||||
func (s *SmartContract) Burn(ctx contractapi.TransactionContextInterface, account string, id uint64, amount uint64) error {
|
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" {
|
if account == "0x0" {
|
||||||
return fmt.Errorf("burn to the zero address")
|
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
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -214,6 +248,15 @@ func (s *SmartContract) Burn(ctx contractapi.TransactionContextInterface, accoun
|
||||||
// This function emits a TransferBatch event.
|
// This function emits a TransferBatch event.
|
||||||
func (s *SmartContract) BurnBatch(ctx contractapi.TransactionContextInterface, account string, ids []uint64, amounts []uint64) error {
|
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" {
|
if account == "0x0" {
|
||||||
return fmt.Errorf("burn to the zero address")
|
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
|
// 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 {
|
if err != nil {
|
||||||
return err
|
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
|
// recipient account must be a valid clientID as returned by the ClientID() function
|
||||||
// This function triggers a TransferSingle event
|
// This function triggers a TransferSingle event
|
||||||
func (s *SmartContract) TransferFrom(ctx contractapi.TransactionContextInterface, sender string, recipient string, id uint64, amount uint64) error {
|
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 {
|
if sender == recipient {
|
||||||
return fmt.Errorf("transfer to self")
|
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
|
// recipient account must be a valid clientID as returned by the ClientID() function
|
||||||
// This function triggers a TransferBatch event
|
// This function triggers a TransferBatch event
|
||||||
func (s *SmartContract) BatchTransferFrom(ctx contractapi.TransactionContextInterface, sender string, recipient string, ids []uint64, amounts []uint64) error {
|
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 {
|
if sender == recipient {
|
||||||
return fmt.Errorf("transfer to self")
|
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
|
amountToSend := make(map[uint64]uint64) // token id => amount
|
||||||
|
|
||||||
for i := 0; i < len(amounts); i++ {
|
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
|
// 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
|
// This function triggers a TransferBatchMultiRecipient event
|
||||||
func (s *SmartContract) BatchTransferFromMultiRecipient(ctx contractapi.TransactionContextInterface, sender string, recipients []string, ids []uint64, amounts []uint64) error {
|
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) {
|
if len(recipients) != len(ids) || len(ids) != len(amounts) {
|
||||||
return fmt.Errorf("recipients, ids, and amounts must have the same length")
|
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
|
amountToSend := make(map[ToID]uint64) // (recipient, id ) => amount
|
||||||
|
|
||||||
for i := 0; i < len(amounts); i++ {
|
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
|
// 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.
|
// _isApprovedForAll returns true if operator is approved to transfer account's tokens.
|
||||||
func _isApprovedForAll(ctx contractapi.TransactionContextInterface, account string, operator string) (bool, error) {
|
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})
|
approvalKey, err := ctx.GetStub().CreateCompositeKey(approvalPrefix, []string{account, operator})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to create the composite key for prefix %s: %v", approvalPrefix, err)
|
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.
|
// SetApprovalForAll returns true if operator is approved to transfer account's tokens.
|
||||||
func (s *SmartContract) SetApprovalForAll(ctx contractapi.TransactionContextInterface, operator string, approved bool) error {
|
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
|
// Get ID of submitting client identity
|
||||||
account, err := ctx.GetClientIdentity().GetID()
|
account, err := ctx.GetClientIdentity().GetID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -490,11 +588,31 @@ func (s *SmartContract) SetApprovalForAll(ctx contractapi.TransactionContextInte
|
||||||
|
|
||||||
// BalanceOf returns the balance of the given account
|
// BalanceOf returns the balance of the given account
|
||||||
func (s *SmartContract) BalanceOf(ctx contractapi.TransactionContextInterface, account string, id uint64) (uint64, error) {
|
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)
|
return balanceOfHelper(ctx, account, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BalanceOfBatch returns the balance of multiple account/token pairs
|
// BalanceOfBatch returns the balance of multiple account/token pairs
|
||||||
func (s *SmartContract) BalanceOfBatch(ctx contractapi.TransactionContextInterface, accounts []string, ids []uint64) ([]uint64, error) {
|
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) {
|
if len(accounts) != len(ids) {
|
||||||
return nil, fmt.Errorf("accounts and ids must have the same length")
|
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
|
// ClientAccountBalance returns the balance of the requesting client's account
|
||||||
func (s *SmartContract) ClientAccountBalance(ctx contractapi.TransactionContextInterface, id uint64) (uint64, error) {
|
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
|
// Get ID of submitting client identity
|
||||||
clientID, err := ctx.GetClientIdentity().GetID()
|
clientID, err := ctx.GetClientIdentity().GetID()
|
||||||
if err != nil {
|
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
|
// 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) {
|
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
|
// Get ID of submitting client identity
|
||||||
clientAccountID, err := ctx.GetClientIdentity().GetID()
|
clientAccountID, err := ctx.GetClientIdentity().GetID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -542,8 +678,17 @@ func (s *SmartContract) ClientAccountID(ctx contractapi.TransactionContextInterf
|
||||||
// This function triggers URI event for each token id
|
// This function triggers URI event for each token id
|
||||||
func (s *SmartContract) SetURI(ctx contractapi.TransactionContextInterface, uri string) error {
|
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
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -563,6 +708,15 @@ func (s *SmartContract) SetURI(ctx contractapi.TransactionContextInterface, uri
|
||||||
// URI returns the URI
|
// URI returns the URI
|
||||||
func (s *SmartContract) URI(ctx contractapi.TransactionContextInterface, id uint64) (string, error) {
|
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)
|
uriBytes, err := ctx.GetStub().GetState(uriKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to get uri: %v", err)
|
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 {
|
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
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -594,6 +757,84 @@ func (s *SmartContract) BroadcastTokenExistance(ctx contractapi.TransactionConte
|
||||||
return emitTransferSingle(ctx, transferSingleEvent)
|
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
|
// Helper Functions
|
||||||
|
|
||||||
// authorizationHelper checks minter authorization - this sample assumes Org1 is the central banker with privilege to mint new tokens
|
// 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, _ = 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)))
|
err = ctx.GetStub().PutState(balanceKey, []byte(strconv.FormatUint(uint64(balance), 10)))
|
||||||
if err != nil {
|
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 {
|
func removeBalance(ctx contractapi.TransactionContextInterface, sender string, ids []uint64, amounts []uint64) error {
|
||||||
// Calculate the total amount of each token to withdraw
|
// Calculate the total amount of each token to withdraw
|
||||||
necessaryFunds := make(map[uint64]uint64) // token id -> necessary amount
|
necessaryFunds := make(map[uint64]uint64) // token id -> necessary amount
|
||||||
|
var err error
|
||||||
|
|
||||||
for i := 0; i < len(amounts); i++ {
|
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
|
// 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)
|
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)
|
_, compositeKeyParts, err := ctx.GetStub().SplitCompositeKey(queryResponse.Key)
|
||||||
if err != nil {
|
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)
|
return fmt.Errorf("sender has insufficient funds for token %v, needed funds: %v, available fund: %v", tokenId, neededAmount, partialBalance)
|
||||||
} else if partialBalance > neededAmount {
|
} else if partialBalance > neededAmount {
|
||||||
// Send the remainder back to the sender
|
// Send the remainder back to the sender
|
||||||
remainder := partialBalance - neededAmount
|
remainder, err := sub(partialBalance, neededAmount)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if selfRecipientKeyNeedsToBeRemoved {
|
if selfRecipientKeyNeedsToBeRemoved {
|
||||||
// Set balance for the key that has the same address for sender and recipient
|
// Set balance for the key that has the same address for sender and recipient
|
||||||
err = setBalance(ctx, sender, sender, tokenId, remainder)
|
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)
|
balAmount, _ := strconv.ParseUint(string(queryResponse.Value), 10, 64)
|
||||||
balance += balAmount
|
balance, err = add(balance, balAmount)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return balance, nil
|
return balance, nil
|
||||||
|
|
@ -859,3 +1117,42 @@ func sortedKeysToID(m map[ToID]uint64) []ToID {
|
||||||
})
|
})
|
||||||
return keys
|
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
|
// Define key names for options
|
||||||
|
const nameKey = "name"
|
||||||
|
const symbolKey = "symbol"
|
||||||
|
const decimalsKey = "decimals"
|
||||||
const totalSupplyKey = "totalSupply"
|
const totalSupplyKey = "totalSupply"
|
||||||
|
|
||||||
// Define objectType names for prefix
|
// Define objectType names for prefix
|
||||||
const allowancePrefix = "allowance"
|
const allowancePrefix = "allowance"
|
||||||
|
|
||||||
|
// Define key names for options
|
||||||
|
|
||||||
|
|
||||||
// SmartContract provides functions for transferring tokens between accounts
|
// SmartContract provides functions for transferring tokens between accounts
|
||||||
type SmartContract struct {
|
type SmartContract struct {
|
||||||
contractapi.Contract
|
contractapi.Contract
|
||||||
|
|
@ -32,6 +37,15 @@ type event struct {
|
||||||
// This function triggers a Transfer event
|
// This function triggers a Transfer event
|
||||||
func (s *SmartContract) Mint(ctx contractapi.TransactionContextInterface, amount int) error {
|
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
|
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to mint new tokens
|
||||||
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
||||||
if err != nil {
|
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.
|
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)))
|
err = ctx.GetStub().PutState(minter, []byte(strconv.Itoa(updatedBalance)))
|
||||||
if err != nil {
|
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
|
// 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)))
|
err = ctx.GetStub().PutState(totalSupplyKey, []byte(strconv.Itoa(totalSupply)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -114,6 +135,14 @@ func (s *SmartContract) Mint(ctx contractapi.TransactionContextInterface, amount
|
||||||
// This function triggers a Transfer event
|
// This function triggers a Transfer event
|
||||||
func (s *SmartContract) Burn(ctx contractapi.TransactionContextInterface, amount int) error {
|
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
|
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to burn new tokens
|
||||||
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
||||||
if err != nil {
|
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.
|
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)))
|
err = ctx.GetStub().PutState(minter, []byte(strconv.Itoa(updatedBalance)))
|
||||||
if err != nil {
|
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.
|
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
|
// 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)))
|
err = ctx.GetStub().PutState(totalSupplyKey, []byte(strconv.Itoa(totalSupply)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -195,6 +231,15 @@ func (s *SmartContract) Burn(ctx contractapi.TransactionContextInterface, amount
|
||||||
// This function triggers a Transfer event
|
// This function triggers a Transfer event
|
||||||
func (s *SmartContract) Transfer(ctx contractapi.TransactionContextInterface, recipient string, amount int) error {
|
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
|
// Get ID of submitting client identity
|
||||||
clientID, err := ctx.GetClientIdentity().GetID()
|
clientID, err := ctx.GetClientIdentity().GetID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -222,6 +267,16 @@ func (s *SmartContract) Transfer(ctx contractapi.TransactionContextInterface, re
|
||||||
|
|
||||||
// BalanceOf returns the balance of the given account
|
// BalanceOf returns the balance of the given account
|
||||||
func (s *SmartContract) BalanceOf(ctx contractapi.TransactionContextInterface, account string) (int, error) {
|
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)
|
balanceBytes, err := ctx.GetStub().GetState(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("failed to read from world state: %v", err)
|
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
|
// ClientAccountBalance returns the balance of the requesting client's account
|
||||||
func (s *SmartContract) ClientAccountBalance(ctx contractapi.TransactionContextInterface) (int, error) {
|
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
|
// Get ID of submitting client identity
|
||||||
clientID, err := ctx.GetClientIdentity().GetID()
|
clientID, err := ctx.GetClientIdentity().GetID()
|
||||||
if err != nil {
|
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
|
// 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) {
|
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
|
// Get ID of submitting client identity
|
||||||
clientAccountID, err := ctx.GetClientIdentity().GetID()
|
clientAccountID, err := ctx.GetClientIdentity().GetID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -274,6 +347,15 @@ func (s *SmartContract) ClientAccountID(ctx contractapi.TransactionContextInterf
|
||||||
// TotalSupply returns the total token supply
|
// TotalSupply returns the total token supply
|
||||||
func (s *SmartContract) TotalSupply(ctx contractapi.TransactionContextInterface) (int, error) {
|
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
|
// Retrieve total supply of tokens from state of smart contract
|
||||||
totalSupplyBytes, err := ctx.GetStub().GetState(totalSupplyKey)
|
totalSupplyBytes, err := ctx.GetStub().GetState(totalSupplyKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -299,6 +381,15 @@ func (s *SmartContract) TotalSupply(ctx contractapi.TransactionContextInterface)
|
||||||
// This function triggers an Approval event
|
// This function triggers an Approval event
|
||||||
func (s *SmartContract) Approve(ctx contractapi.TransactionContextInterface, spender string, value int) error {
|
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
|
// Get ID of submitting client identity
|
||||||
owner, err := ctx.GetClientIdentity().GetID()
|
owner, err := ctx.GetClientIdentity().GetID()
|
||||||
if err != nil {
|
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
|
// 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) {
|
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
|
// Create allowanceKey
|
||||||
allowanceKey, err := ctx.GetStub().CreateCompositeKey(allowancePrefix, []string{owner, spender})
|
allowanceKey, err := ctx.GetStub().CreateCompositeKey(allowancePrefix, []string{owner, spender})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -366,6 +466,15 @@ func (s *SmartContract) Allowance(ctx contractapi.TransactionContextInterface, o
|
||||||
// This function triggers a Transfer event
|
// This function triggers a Transfer event
|
||||||
func (s *SmartContract) TransferFrom(ctx contractapi.TransactionContextInterface, from string, to string, value int) error {
|
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
|
// Get ID of submitting client identity
|
||||||
spender, err := ctx.GetClientIdentity().GetID()
|
spender, err := ctx.GetClientIdentity().GetID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -399,7 +508,11 @@ func (s *SmartContract) TransferFrom(ctx contractapi.TransactionContextInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrease the allowance
|
// 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)))
|
err = ctx.GetStub().PutState(allowanceKey, []byte(strconv.Itoa(updatedAllowance)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -421,6 +534,90 @@ func (s *SmartContract) TransferFrom(ctx contractapi.TransactionContextInterface
|
||||||
return nil
|
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
|
// Helper Functions
|
||||||
|
|
||||||
// transferHelper is a helper function that transfers tokens from the "from" address to the "to" address
|
// 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.
|
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
|
fromUpdatedBalance, err := sub(fromCurrentBalance, value)
|
||||||
toUpdatedBalance := toCurrentBalance + 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)))
|
err = ctx.GetStub().PutState(from, []byte(strconv.Itoa(fromUpdatedBalance)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -481,3 +685,44 @@ func transferHelper(ctx contractapi.TransactionContextInterface, from string, to
|
||||||
|
|
||||||
return nil
|
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"),
|
BALANCE_PREFIX("balance"),
|
||||||
ALLOWANCE_PREFIX("allowance"),
|
ALLOWANCE_PREFIX("allowance"),
|
||||||
NAME_KEY("name"),
|
NAME_KEY("name"),
|
||||||
SYMBOL_KEY("decimals"),
|
SYMBOL_KEY("symbolKey"),
|
||||||
DECIMALS_KEY("symbolKey"),
|
DECIMALS_KEY("decimals"),
|
||||||
TOTAL_SUPPLY_KEY("totalSupply"),
|
TOTAL_SUPPLY_KEY("totalSupply"),
|
||||||
TRANSFER_EVENT("Transfer"),
|
TRANSFER_EVENT("Transfer"),
|
||||||
MINTER_ORG_MSPID("Org1MSP"),
|
MINTER_ORG_MSPID("Org1MSP"),
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,10 @@ public final class ERC20TokenContract implements ContractInterface {
|
||||||
throw new ChaincodeException(
|
throw new ChaincodeException(
|
||||||
"Client is not authorized to mint new tokens", UNAUTHORIZED_SENDER.toString());
|
"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
|
// Get ID of submitting client identity
|
||||||
String minter = ctx.getClientIdentity().getId();
|
String minter = ctx.getClientIdentity().getId();
|
||||||
if (amount <= 0) {
|
if (amount <= 0) {
|
||||||
|
|
@ -127,6 +131,10 @@ public final class ERC20TokenContract implements ContractInterface {
|
||||||
throw new ChaincodeException(
|
throw new ChaincodeException(
|
||||||
"Client is not authorized to burn tokens", UNAUTHORIZED_SENDER.toString());
|
"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();
|
String minter = ctx.getClientIdentity().getId();
|
||||||
if (amount <= 0) {
|
if (amount <= 0) {
|
||||||
throw new ChaincodeException(
|
throw new ChaincodeException(
|
||||||
|
|
@ -173,6 +181,8 @@ public final class ERC20TokenContract implements ContractInterface {
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||||
public void Transfer(final Context ctx, final String to, final long value) {
|
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();
|
String from = ctx.getClientIdentity().getId();
|
||||||
this.transferHelper(ctx, from, to, value);
|
this.transferHelper(ctx, from, to, value);
|
||||||
final Transfer transferEvent = new Transfer(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)
|
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||||
public long BalanceOf(final Context ctx, final String owner) {
|
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();
|
ChaincodeStub stub = ctx.getStub();
|
||||||
CompositeKey balanceKey = stub.createCompositeKey(BALANCE_PREFIX.getValue(), owner);
|
CompositeKey balanceKey = stub.createCompositeKey(BALANCE_PREFIX.getValue(), owner);
|
||||||
String balance = stub.getStringState(balanceKey.toString());
|
String balance = stub.getStringState(balanceKey.toString());
|
||||||
|
|
@ -207,6 +219,8 @@ public final class ERC20TokenContract implements ContractInterface {
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||||
public long ClientAccountBalance(final Context ctx) {
|
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
|
// Get ID of submitting client identity
|
||||||
ChaincodeStub stub = ctx.getStub();
|
ChaincodeStub stub = ctx.getStub();
|
||||||
String clientAccountID = ctx.getClientIdentity().getId();
|
String clientAccountID = ctx.getClientIdentity().getId();
|
||||||
|
|
@ -230,6 +244,8 @@ public final class ERC20TokenContract implements ContractInterface {
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||||
public String ClientAccountID(final Context ctx) {
|
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
|
// Get ID of submitting client identity
|
||||||
return ctx.getClientIdentity().getId();
|
return ctx.getClientIdentity().getId();
|
||||||
}
|
}
|
||||||
|
|
@ -242,6 +258,8 @@ public final class ERC20TokenContract implements ContractInterface {
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||||
public long TotalSupply(final Context ctx) {
|
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());
|
String totalSupply = ctx.getStub().getStringState(TOTAL_SUPPLY_KEY.getValue());
|
||||||
if (stringIsNullOrEmpty(totalSupply)) {
|
if (stringIsNullOrEmpty(totalSupply)) {
|
||||||
throw new ChaincodeException("Total Supply not found", NOT_FOUND.toString());
|
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)
|
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||||
public void Approve(final Context ctx, final String spender, final long value) {
|
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();
|
ChaincodeStub stub = ctx.getStub();
|
||||||
String owner = ctx.getClientIdentity().getId();
|
String owner = ctx.getClientIdentity().getId();
|
||||||
CompositeKey allowanceKey =
|
CompositeKey allowanceKey =
|
||||||
|
|
@ -282,6 +302,8 @@ public final class ERC20TokenContract implements ContractInterface {
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||||
public long Allowance(final Context ctx, final String owner, final String spender) {
|
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();
|
ChaincodeStub stub = ctx.getStub();
|
||||||
CompositeKey allowanceKey =
|
CompositeKey allowanceKey =
|
||||||
stub.createCompositeKey(ALLOWANCE_PREFIX.getValue(), owner, spender);
|
stub.createCompositeKey(ALLOWANCE_PREFIX.getValue(), owner, spender);
|
||||||
|
|
@ -308,6 +330,8 @@ public final class ERC20TokenContract implements ContractInterface {
|
||||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||||
public void TransferFrom(
|
public void TransferFrom(
|
||||||
final Context ctx, final String from, final String to, final long value) {
|
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();
|
String spender = ctx.getClientIdentity().getId();
|
||||||
ChaincodeStub stub = ctx.getStub();
|
ChaincodeStub stub = ctx.getStub();
|
||||||
// Retrieve the allowance of the spender
|
// Retrieve the allowance of the spender
|
||||||
|
|
@ -402,9 +426,23 @@ public final class ERC20TokenContract implements ContractInterface {
|
||||||
* @param decimals The decimals of the token
|
* @param decimals The decimals of the token
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||||
public void SetOptions(
|
public void Initialize(
|
||||||
final Context ctx, final String name, final String symbol, final String decimals) {
|
final Context ctx, final String name, final String symbol, final String decimals) {
|
||||||
ChaincodeStub stub = ctx.getStub();
|
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(NAME_KEY.getValue(), name);
|
||||||
stub.putStringState(SYMBOL_KEY.getValue(), symbol);
|
stub.putStringState(SYMBOL_KEY.getValue(), symbol);
|
||||||
stub.putStringState(DECIMALS_KEY.getValue(), decimals);
|
stub.putStringState(DECIMALS_KEY.getValue(), decimals);
|
||||||
|
|
@ -420,6 +458,8 @@ public final class ERC20TokenContract implements ContractInterface {
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||||
public String TokenName(final Context ctx) {
|
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());
|
String tokenName = ctx.getStub().getStringState(ContractConstants.NAME_KEY.getValue());
|
||||||
if (stringIsNullOrEmpty(tokenName)) {
|
if (stringIsNullOrEmpty(tokenName)) {
|
||||||
throw new ChaincodeException("Token name not found", NOT_FOUND.toString());
|
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)
|
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||||
public String TokenSymbol(final Context ctx) {
|
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());
|
String tokenSymbol = ctx.getStub().getStringState(SYMBOL_KEY.getValue());
|
||||||
if (stringIsNullOrEmpty(tokenSymbol)) {
|
if (stringIsNullOrEmpty(tokenSymbol)) {
|
||||||
throw new ChaincodeException("Token symbol not found", NOT_FOUND.toString());
|
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)
|
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||||
public int Decimals(final Context ctx) {
|
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());
|
String decimals = ctx.getStub().getStringState(DECIMALS_KEY.getValue());
|
||||||
if (stringIsNullOrEmpty(decimals)) {
|
if (stringIsNullOrEmpty(decimals)) {
|
||||||
throw new ChaincodeException("Decimal not found", NOT_FOUND.toString());
|
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) {
|
private byte[] marshal(final Object obj) {
|
||||||
return new Genson().serialize(obj).getBytes(UTF_8);
|
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)
|
assertThat(thrown)
|
||||||
.isInstanceOf(ChaincodeException.class)
|
.isInstanceOf(ChaincodeException.class)
|
||||||
.hasNoCause()
|
.hasNoCause()
|
||||||
.hasMessage("Token name not found");
|
.hasMessage("Contract options need to be set before calling any function, call Initialize() to initialize contract");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -67,6 +67,7 @@ public class TokenERC20ContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(stub.getStringState(SYMBOL_KEY.getValue())).thenReturn("ARBT");
|
when(stub.getStringState(SYMBOL_KEY.getValue())).thenReturn("ARBT");
|
||||||
String toknName = contract.TokenSymbol(ctx);
|
String toknName = contract.TokenSymbol(ctx);
|
||||||
assertThat(toknName).isEqualTo("ARBT");
|
assertThat(toknName).isEqualTo("ARBT");
|
||||||
|
|
@ -78,6 +79,7 @@ public class TokenERC20ContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(stub.getStringState(SYMBOL_KEY.getValue())).thenReturn("");
|
when(stub.getStringState(SYMBOL_KEY.getValue())).thenReturn("");
|
||||||
Throwable thrown = catchThrowable(() -> contract.TokenSymbol(ctx));
|
Throwable thrown = catchThrowable(() -> contract.TokenSymbol(ctx));
|
||||||
assertThat(thrown)
|
assertThat(thrown)
|
||||||
|
|
@ -92,6 +94,7 @@ public class TokenERC20ContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(stub.getStringState(DECIMALS_KEY.getValue())).thenReturn("18");
|
when(stub.getStringState(DECIMALS_KEY.getValue())).thenReturn("18");
|
||||||
long decimal = contract.Decimals(ctx);
|
long decimal = contract.Decimals(ctx);
|
||||||
assertThat(decimal).isEqualTo(18);
|
assertThat(decimal).isEqualTo(18);
|
||||||
|
|
@ -103,6 +106,7 @@ public class TokenERC20ContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(stub.getStringState(DECIMALS_KEY.getValue())).thenReturn("");
|
when(stub.getStringState(DECIMALS_KEY.getValue())).thenReturn("");
|
||||||
Throwable thrown = catchThrowable(() -> contract.Decimals(ctx));
|
Throwable thrown = catchThrowable(() -> contract.Decimals(ctx));
|
||||||
assertThat(thrown)
|
assertThat(thrown)
|
||||||
|
|
@ -117,6 +121,7 @@ public class TokenERC20ContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(stub.getStringState(TOTAL_SUPPLY_KEY.getValue())).thenReturn("222222222222");
|
when(stub.getStringState(TOTAL_SUPPLY_KEY.getValue())).thenReturn("222222222222");
|
||||||
long totalSupply = contract.TotalSupply(ctx);
|
long totalSupply = contract.TotalSupply(ctx);
|
||||||
assertThat(totalSupply).isEqualTo(222222222222L);
|
assertThat(totalSupply).isEqualTo(222222222222L);
|
||||||
|
|
@ -128,6 +133,7 @@ public class TokenERC20ContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(stub.getStringState(TOTAL_SUPPLY_KEY.getValue())).thenReturn("");
|
when(stub.getStringState(TOTAL_SUPPLY_KEY.getValue())).thenReturn("");
|
||||||
Throwable thrown = catchThrowable(() -> contract.TotalSupply(ctx));
|
Throwable thrown = catchThrowable(() -> contract.TotalSupply(ctx));
|
||||||
assertThat(thrown)
|
assertThat(thrown)
|
||||||
|
|
@ -143,6 +149,7 @@ public class TokenERC20ContractTest {
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
ClientIdentity ci = mock(ClientIdentity.class);
|
ClientIdentity ci = mock(ClientIdentity.class);
|
||||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||||
when(ci.getId()).thenReturn(org1UserId);
|
when(ci.getId()).thenReturn(org1UserId);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
|
@ -156,12 +163,15 @@ public class TokenERC20ContractTest {
|
||||||
class TokenOperationsInvoke {
|
class TokenOperationsInvoke {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void invokeSetOptionsTest() {
|
public void invokeInitializeTest() {
|
||||||
ERC20TokenContract contract = new ERC20TokenContract();
|
ERC20TokenContract contract = new ERC20TokenContract();
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
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(NAME_KEY.getValue(), "ARBTToken");
|
||||||
verify(stub).putStringState(SYMBOL_KEY.getValue(), "ARBT");
|
verify(stub).putStringState(SYMBOL_KEY.getValue(), "ARBT");
|
||||||
verify(stub).putStringState(DECIMALS_KEY.getValue(), "18");
|
verify(stub).putStringState(DECIMALS_KEY.getValue(), "18");
|
||||||
|
|
@ -173,6 +183,7 @@ public class TokenERC20ContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
ClientIdentity ci = mock(ClientIdentity.class);
|
ClientIdentity ci = mock(ClientIdentity.class);
|
||||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||||
|
|
@ -192,6 +203,7 @@ public class TokenERC20ContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
ClientIdentity ci = mock(ClientIdentity.class);
|
ClientIdentity ci = mock(ClientIdentity.class);
|
||||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||||
|
|
@ -213,6 +225,7 @@ public class TokenERC20ContractTest {
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
ClientIdentity ci = mock(ClientIdentity.class);
|
ClientIdentity ci = mock(ClientIdentity.class);
|
||||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||||
when(ci.getId()).thenReturn(org1UserId);
|
when(ci.getId()).thenReturn(org1UserId);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
|
@ -234,6 +247,7 @@ public class TokenERC20ContractTest {
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
ClientIdentity ci = mock(ClientIdentity.class);
|
ClientIdentity ci = mock(ClientIdentity.class);
|
||||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(ci.getMSPID()).thenReturn("Org2MSP");
|
when(ci.getMSPID()).thenReturn("Org2MSP");
|
||||||
when(ci.getId()).thenReturn(org1UserId);
|
when(ci.getId()).thenReturn(org1UserId);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
|
@ -256,6 +270,7 @@ public class TokenERC20ContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
ClientIdentity ci = mock(ClientIdentity.class);
|
ClientIdentity ci = mock(ClientIdentity.class);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||||
when(ci.getId()).thenReturn(org1UserId);
|
when(ci.getId()).thenReturn(org1UserId);
|
||||||
|
|
@ -284,6 +299,7 @@ public class TokenERC20ContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
ClientIdentity ci = mock(ClientIdentity.class);
|
ClientIdentity ci = mock(ClientIdentity.class);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||||
when(ci.getId()).thenReturn(org1UserId);
|
when(ci.getId()).thenReturn(org1UserId);
|
||||||
|
|
@ -312,6 +328,7 @@ public class TokenERC20ContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
ClientIdentity ci = mock(ClientIdentity.class);
|
ClientIdentity ci = mock(ClientIdentity.class);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||||
when(ci.getId()).thenReturn(org1UserId);
|
when(ci.getId()).thenReturn(org1UserId);
|
||||||
|
|
@ -343,6 +360,7 @@ public class TokenERC20ContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
ClientIdentity ci = mock(ClientIdentity.class);
|
ClientIdentity ci = mock(ClientIdentity.class);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||||
when(ci.getId()).thenReturn(org1UserId);
|
when(ci.getId()).thenReturn(org1UserId);
|
||||||
|
|
@ -374,6 +392,7 @@ public class TokenERC20ContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
ClientIdentity ci = mock(ClientIdentity.class);
|
ClientIdentity ci = mock(ClientIdentity.class);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||||
when(ci.getId()).thenReturn(org1UserId);
|
when(ci.getId()).thenReturn(org1UserId);
|
||||||
|
|
@ -394,6 +413,7 @@ public class TokenERC20ContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
ClientIdentity ci = mock(ClientIdentity.class);
|
ClientIdentity ci = mock(ClientIdentity.class);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||||
when(ci.getMSPID()).thenReturn("Org2MSP");
|
when(ci.getMSPID()).thenReturn("Org2MSP");
|
||||||
when(ci.getId()).thenReturn(spender);
|
when(ci.getId()).thenReturn(spender);
|
||||||
|
|
@ -418,6 +438,7 @@ public class TokenERC20ContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
ClientIdentity ci = mock(ClientIdentity.class);
|
ClientIdentity ci = mock(ClientIdentity.class);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||||
when(ci.getMSPID()).thenReturn("Org1MSP");
|
when(ci.getMSPID()).thenReturn("Org1MSP");
|
||||||
when(ci.getId()).thenReturn(org1UserId);
|
when(ci.getId()).thenReturn(org1UserId);
|
||||||
|
|
@ -446,6 +467,7 @@ public class TokenERC20ContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
ClientIdentity ci = mock(ClientIdentity.class);
|
ClientIdentity ci = mock(ClientIdentity.class);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||||
when(ci.getId()).thenReturn(org1UserId);
|
when(ci.getId()).thenReturn(org1UserId);
|
||||||
|
|
@ -465,6 +487,7 @@ public class TokenERC20ContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
ClientIdentity ci = mock(ClientIdentity.class);
|
ClientIdentity ci = mock(ClientIdentity.class);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||||
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
when(ci.getMSPID()).thenReturn(MINTER_ORG_MSPID.getValue());
|
||||||
when(ci.getId()).thenReturn(org1UserId);
|
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";
|
+ " C=US::CN=ca.org2.example.com, O=org2.example.com, L=San Francisco, ST=California, C=US";
|
||||||
|
|
||||||
CompositeKey ckFromBalance = mock(CompositeKey.class);
|
CompositeKey ckFromBalance = mock(CompositeKey.class);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(stub.createCompositeKey(BALANCE_PREFIX.toString(), org1UserId))
|
when(stub.createCompositeKey(BALANCE_PREFIX.toString(), org1UserId))
|
||||||
.thenReturn(ckFromBalance);
|
.thenReturn(ckFromBalance);
|
||||||
when(ckFromBalance.toString()).thenReturn(BALANCE_PREFIX.getValue() + org1UserId);
|
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";
|
+ " C=US::CN=ca.org2.example.com, O=org2.example.com, L=San Francisco, ST=California, C=US";
|
||||||
|
|
||||||
CompositeKey ckFromBalance = mock(CompositeKey.class);
|
CompositeKey ckFromBalance = mock(CompositeKey.class);
|
||||||
|
when(stub.getStringState(NAME_KEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(stub.createCompositeKey(BALANCE_PREFIX.getValue(), org1UserId))
|
when(stub.createCompositeKey(BALANCE_PREFIX.getValue(), org1UserId))
|
||||||
.thenReturn(ckFromBalance);
|
.thenReturn(ckFromBalance);
|
||||||
when(ckFromBalance.toString()).thenReturn(BALANCE_PREFIX.getValue() + org1UserId);
|
when(ckFromBalance.toString()).thenReturn(BALANCE_PREFIX.getValue() + org1UserId);
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,12 @@ class TokenERC20Contract extends Contract {
|
||||||
* @returns {String} Returns the name of the token
|
* @returns {String} Returns the name of the token
|
||||||
*/
|
*/
|
||||||
async TokenName(ctx) {
|
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);
|
const nameBytes = await ctx.stub.getState(nameKey);
|
||||||
|
|
||||||
return nameBytes.toString();
|
return nameBytes.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,6 +46,10 @@ class TokenERC20Contract extends Contract {
|
||||||
* @returns {String} Returns the symbol of the token
|
* @returns {String} Returns the symbol of the token
|
||||||
*/
|
*/
|
||||||
async Symbol(ctx) {
|
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);
|
const symbolBytes = await ctx.stub.getState(symbolKey);
|
||||||
return symbolBytes.toString();
|
return symbolBytes.toString();
|
||||||
}
|
}
|
||||||
|
|
@ -53,6 +62,10 @@ class TokenERC20Contract extends Contract {
|
||||||
* @returns {Number} Returns the number of decimals
|
* @returns {Number} Returns the number of decimals
|
||||||
*/
|
*/
|
||||||
async Decimals(ctx) {
|
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 decimalsBytes = await ctx.stub.getState(decimalsKey);
|
||||||
const decimals = parseInt(decimalsBytes.toString());
|
const decimals = parseInt(decimalsBytes.toString());
|
||||||
return decimals;
|
return decimals;
|
||||||
|
|
@ -65,6 +78,10 @@ class TokenERC20Contract extends Contract {
|
||||||
* @returns {Number} Returns the total token supply
|
* @returns {Number} Returns the total token supply
|
||||||
*/
|
*/
|
||||||
async TotalSupply(ctx) {
|
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 totalSupplyBytes = await ctx.stub.getState(totalSupplyKey);
|
||||||
const totalSupply = parseInt(totalSupplyBytes.toString());
|
const totalSupply = parseInt(totalSupplyBytes.toString());
|
||||||
return totalSupply;
|
return totalSupply;
|
||||||
|
|
@ -78,6 +95,10 @@ class TokenERC20Contract extends Contract {
|
||||||
* @returns {Number} Returns the account balance
|
* @returns {Number} Returns the account balance
|
||||||
*/
|
*/
|
||||||
async BalanceOf(ctx, owner) {
|
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 balanceKey = ctx.stub.createCompositeKey(balancePrefix, [owner]);
|
||||||
|
|
||||||
const balanceBytes = await ctx.stub.getState(balanceKey);
|
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
|
* @returns {Boolean} Return whether the transfer was successful or not
|
||||||
*/
|
*/
|
||||||
async Transfer(ctx, to, value) {
|
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 from = ctx.clientIdentity.getID();
|
||||||
|
|
||||||
const transferResp = await this._transfer(ctx, from, to, value);
|
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
|
* @returns {Boolean} Return whether the transfer was successful or not
|
||||||
*/
|
*/
|
||||||
async TransferFrom(ctx, from, to, value) {
|
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();
|
const spender = ctx.clientIdentity.getID();
|
||||||
|
|
||||||
// Retrieve the allowance of the spender
|
// Retrieve the allowance of the spender
|
||||||
|
|
@ -149,7 +178,7 @@ class TokenERC20Contract extends Contract {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrease the allowance
|
// Decrease the allowance
|
||||||
const updatedAllowance = currentAllowance - valueInt;
|
const updatedAllowance = this.sub(currentAllowance, valueInt);
|
||||||
await ctx.stub.putState(allowanceKey, Buffer.from(updatedAllowance.toString()));
|
await ctx.stub.putState(allowanceKey, Buffer.from(updatedAllowance.toString()));
|
||||||
console.log(`spender ${spender} allowance updated from ${currentAllowance} to ${updatedAllowance}`);
|
console.log(`spender ${spender} allowance updated from ${currentAllowance} to ${updatedAllowance}`);
|
||||||
|
|
||||||
|
|
@ -202,8 +231,8 @@ class TokenERC20Contract extends Contract {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the balance
|
// Update the balance
|
||||||
const fromUpdatedBalance = fromCurrentBalance - valueInt;
|
const fromUpdatedBalance = this.sub(fromCurrentBalance, valueInt);
|
||||||
const toUpdatedBalance = toCurrentBalance + valueInt;
|
const toUpdatedBalance = this.add(toCurrentBalance, valueInt);
|
||||||
|
|
||||||
await ctx.stub.putState(fromBalanceKey, Buffer.from(fromUpdatedBalance.toString()));
|
await ctx.stub.putState(fromBalanceKey, Buffer.from(fromUpdatedBalance.toString()));
|
||||||
await ctx.stub.putState(toBalanceKey, Buffer.from(toUpdatedBalance.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
|
* @returns {Boolean} Return whether the approval was successful or not
|
||||||
*/
|
*/
|
||||||
async Approve(ctx, spender, value) {
|
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 owner = ctx.clientIdentity.getID();
|
||||||
|
|
||||||
const allowanceKey = ctx.stub.createCompositeKey(allowancePrefix, [owner, spender]);
|
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
|
* @returns {Number} Return the amount of remaining tokens allowed to spent
|
||||||
*/
|
*/
|
||||||
async Allowance(ctx, owner, spender) {
|
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 allowanceKey = ctx.stub.createCompositeKey(allowancePrefix, [owner, spender]);
|
||||||
|
|
||||||
const allowanceBytes = await ctx.stub.getState(allowanceKey);
|
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} decimals The decimals of the token
|
||||||
* @param {String} totalSupply The totalSupply 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(nameKey, Buffer.from(name));
|
||||||
await ctx.stub.putState(symbolKey, Buffer.from(symbol));
|
await ctx.stub.putState(symbolKey, Buffer.from(symbol));
|
||||||
await ctx.stub.putState(decimalsKey, Buffer.from(decimals));
|
await ctx.stub.putState(decimalsKey, Buffer.from(decimals));
|
||||||
|
|
@ -287,6 +336,9 @@ class TokenERC20Contract extends Contract {
|
||||||
*/
|
*/
|
||||||
async Mint(ctx, amount) {
|
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
|
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to mint new tokens
|
||||||
const clientMSPID = ctx.clientIdentity.getMSPID();
|
const clientMSPID = ctx.clientIdentity.getMSPID();
|
||||||
if (clientMSPID !== 'Org1MSP') {
|
if (clientMSPID !== 'Org1MSP') {
|
||||||
|
|
@ -311,7 +363,7 @@ class TokenERC20Contract extends Contract {
|
||||||
} else {
|
} else {
|
||||||
currentBalance = parseInt(currentBalanceBytes.toString());
|
currentBalance = parseInt(currentBalanceBytes.toString());
|
||||||
}
|
}
|
||||||
const updatedBalance = currentBalance + amountInt;
|
const updatedBalance = this.add(currentBalance, amountInt);
|
||||||
|
|
||||||
await ctx.stub.putState(balanceKey, Buffer.from(updatedBalance.toString()));
|
await ctx.stub.putState(balanceKey, Buffer.from(updatedBalance.toString()));
|
||||||
|
|
||||||
|
|
@ -324,7 +376,7 @@ class TokenERC20Contract extends Contract {
|
||||||
} else {
|
} else {
|
||||||
totalSupply = parseInt(totalSupplyBytes.toString());
|
totalSupply = parseInt(totalSupplyBytes.toString());
|
||||||
}
|
}
|
||||||
totalSupply = totalSupply + amountInt;
|
totalSupply = this.add(totalSupply, amountInt);
|
||||||
await ctx.stub.putState(totalSupplyKey, Buffer.from(totalSupply.toString()));
|
await ctx.stub.putState(totalSupplyKey, Buffer.from(totalSupply.toString()));
|
||||||
|
|
||||||
// Emit the Transfer event
|
// Emit the Transfer event
|
||||||
|
|
@ -344,6 +396,9 @@ class TokenERC20Contract extends Contract {
|
||||||
*/
|
*/
|
||||||
async Burn(ctx, amount) {
|
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
|
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to burn tokens
|
||||||
const clientMSPID = ctx.clientIdentity.getMSPID();
|
const clientMSPID = ctx.clientIdentity.getMSPID();
|
||||||
if (clientMSPID !== 'Org1MSP') {
|
if (clientMSPID !== 'Org1MSP') {
|
||||||
|
|
@ -361,7 +416,7 @@ class TokenERC20Contract extends Contract {
|
||||||
throw new Error('The balance does not exist');
|
throw new Error('The balance does not exist');
|
||||||
}
|
}
|
||||||
const currentBalance = parseInt(currentBalanceBytes.toString());
|
const currentBalance = parseInt(currentBalanceBytes.toString());
|
||||||
const updatedBalance = currentBalance - amountInt;
|
const updatedBalance = this.sub(currentBalance, amountInt);
|
||||||
|
|
||||||
await ctx.stub.putState(balanceKey, Buffer.from(updatedBalance.toString()));
|
await ctx.stub.putState(balanceKey, Buffer.from(updatedBalance.toString()));
|
||||||
|
|
||||||
|
|
@ -370,7 +425,7 @@ class TokenERC20Contract extends Contract {
|
||||||
if (!totalSupplyBytes || totalSupplyBytes.length === 0) {
|
if (!totalSupplyBytes || totalSupplyBytes.length === 0) {
|
||||||
throw new Error('totalSupply does not exist.');
|
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()));
|
await ctx.stub.putState(totalSupplyKey, Buffer.from(totalSupply.toString()));
|
||||||
|
|
||||||
// Emit the Transfer event
|
// Emit the Transfer event
|
||||||
|
|
@ -388,6 +443,10 @@ class TokenERC20Contract extends Contract {
|
||||||
* @returns {Number} Returns the account balance
|
* @returns {Number} Returns the account balance
|
||||||
*/
|
*/
|
||||||
async ClientAccountBalance(ctx) {
|
async ClientAccountBalance(ctx) {
|
||||||
|
|
||||||
|
//check contract options are already set first to execute the function
|
||||||
|
await this.CheckInitialized(ctx);
|
||||||
|
|
||||||
// Get ID of submitting client identity
|
// Get ID of submitting client identity
|
||||||
const clientAccountID = ctx.clientIdentity.getID();
|
const clientAccountID = ctx.clientIdentity.getID();
|
||||||
|
|
||||||
|
|
@ -405,11 +464,40 @@ class TokenERC20Contract extends Contract {
|
||||||
// In this implementation, the client account ID is the clientId itself.
|
// 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
|
// 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) {
|
async ClientAccountID(ctx) {
|
||||||
|
|
||||||
|
//check contract options are already set first to execute the function
|
||||||
|
await this.CheckInitialized(ctx);
|
||||||
|
|
||||||
// Get ID of submitting client identity
|
// Get ID of submitting client identity
|
||||||
const clientAccountID = ctx.clientIdentity.getID();
|
const clientAccountID = ctx.clientIdentity.getID();
|
||||||
return clientAccountID;
|
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;
|
module.exports = TokenERC20Contract;
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ describe('Chaincode', () => {
|
||||||
let mockStub;
|
let mockStub;
|
||||||
let mockClientIdentity;
|
let mockClientIdentity;
|
||||||
|
|
||||||
beforeEach('Sandbox creation', () => {
|
beforeEach('Sandbox creation', async () => {
|
||||||
sandbox = sinon.createSandbox();
|
sandbox = sinon.createSandbox();
|
||||||
token = new TokenERC20Contract('token-erc20');
|
token = new TokenERC20Contract('token-erc20');
|
||||||
|
|
||||||
|
|
@ -36,6 +36,8 @@ describe('Chaincode', () => {
|
||||||
mockClientIdentity = sinon.createStubInstance(ClientIdentity);
|
mockClientIdentity = sinon.createStubInstance(ClientIdentity);
|
||||||
ctx.clientIdentity = mockClientIdentity;
|
ctx.clientIdentity = mockClientIdentity;
|
||||||
|
|
||||||
|
await token.Initialize(ctx, 'some name', 'some symbol', '2');
|
||||||
|
|
||||||
mockStub.putState.resolves('some state');
|
mockStub.putState.resolves('some state');
|
||||||
mockStub.setEvent.returns('set event');
|
mockStub.setEvent.returns('set event');
|
||||||
|
|
||||||
|
|
@ -198,13 +200,18 @@ describe('Chaincode', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#SetOption', () => {
|
describe('#Initialize', () => {
|
||||||
it('should work', async () => {
|
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, 'name', Buffer.from('some name'));
|
||||||
sinon.assert.calledWith(mockStub.putState, 'symbol', Buffer.from('some symbol'));
|
sinon.assert.calledWith(mockStub.putState, 'symbol', Buffer.from('some symbol'));
|
||||||
sinon.assert.calledWith(mockStub.putState, 'decimals', Buffer.from('2'));
|
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
|
// 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
|
// returns {int} The number of non-fungible tokens owned by the owner, possibly zero
|
||||||
func (c *TokenERC721Contract) BalanceOf(ctx contractapi.TransactionContextInterface, owner string) int {
|
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.
|
// 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.*
|
// 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
|
// param {String} tokenId The identifier for a non-fungible token
|
||||||
// returns {String} Return the owner of the non-fungible token
|
// returns {String} Return the owner of the non-fungible token
|
||||||
func (c *TokenERC721Contract) OwnerOf(ctx contractapi.TransactionContextInterface, tokenId string) (string, error) {
|
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)
|
nft, err := _readNFT(ctx, tokenId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("could not process OwnerOf for tokenId: %w", err)
|
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
|
// param {String} tokenId the non-fungible token to approve
|
||||||
// returns {Boolean} Return whether the approval was successful or not
|
// returns {Boolean} Return whether the approval was successful or not
|
||||||
func (c *TokenERC721Contract) Approve(ctx contractapi.TransactionContextInterface, operator string, tokenId string) (bool, error) {
|
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()
|
sender64, err := ctx.GetClientIdentity().GetID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to GetClientIdentity: %v", err)
|
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
|
// param {Boolean} approved True if the operator is approved, false to revoke approval
|
||||||
// returns {Boolean} Return whether the approval was successful or not
|
// returns {Boolean} Return whether the approval was successful or not
|
||||||
func (c *TokenERC721Contract) SetApprovalForAll(ctx contractapi.TransactionContextInterface, operator string, approved bool) (bool, error) {
|
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()
|
sender64, err := ctx.GetClientIdentity().GetID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to GetClientIdentity: %v", err)
|
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
|
// 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
|
// 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) {
|
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})
|
approvalKey, err := ctx.GetStub().CreateCompositeKey(approvalPrefix, []string{owner, operator})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to CreateCompositeKey: %v", err)
|
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
|
// 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
|
// 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) {
|
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)
|
nft, err := _readNFT(ctx, tokenId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "false", fmt.Errorf("failed GetApproved for tokenId : %v", err)
|
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) {
|
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
|
// Get ID of submitting client identity
|
||||||
sender64, err := ctx.GetClientIdentity().GetID()
|
sender64, err := ctx.GetClientIdentity().GetID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -336,6 +406,16 @@ func (c *TokenERC721Contract) TransferFrom(ctx contractapi.TransactionContextInt
|
||||||
// returns {String} Returns the name of the token
|
// returns {String} Returns the name of the token
|
||||||
|
|
||||||
func (c *TokenERC721Contract) Name(ctx contractapi.TransactionContextInterface) (string, error) {
|
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)
|
bytes, err := ctx.GetStub().GetState(nameKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to get Name bytes: %s", err)
|
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
|
// returns {String} Returns the symbol of the token
|
||||||
|
|
||||||
func (c *TokenERC721Contract) Symbol(ctx contractapi.TransactionContextInterface) (string, error) {
|
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)
|
bytes, err := ctx.GetStub().GetState(symbolKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to get Symbol: %v", err)
|
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
|
// returns {String} Returns the URI of the token
|
||||||
|
|
||||||
func (c *TokenERC721Contract) TokenURI(ctx contractapi.TransactionContextInterface, tokenId string) (string, error) {
|
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)
|
nft, err := _readNFT(ctx, tokenId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to get TokenURI: %v", err)
|
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.
|
// where each one of them has an assigned and queryable owner.
|
||||||
|
|
||||||
func (c *TokenERC721Contract) TotalSupply(ctx contractapi.TransactionContextInterface) int {
|
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.
|
// 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.*
|
// TotalSupply() queries for and counts all records matching nftPrefix.*
|
||||||
|
|
||||||
|
|
@ -399,11 +509,11 @@ func (c *TokenERC721Contract) TotalSupply(ctx contractapi.TransactionContextInte
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============== ERC721 enumeration extension ===============
|
// ============== 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} name The name of the token
|
||||||
// param {String} symbol The symbol 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
|
// Check minter authorization - this sample assumes Org1 is the issuer with privilege to set the name and symbol
|
||||||
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
||||||
if err != nil {
|
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")
|
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))
|
err = ctx.GetStub().PutState(nameKey, []byte(name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to PutState nameKey %s: %v", nameKey, err)
|
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) {
|
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
|
// Check minter authorization - this sample assumes Org1 is the issuer with privilege to mint a new token
|
||||||
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -518,6 +645,16 @@ func (c *TokenERC721Contract) MintWithTokenURI(ctx contractapi.TransactionContex
|
||||||
// param {String} tokenId Unique ID of a non-fungible token
|
// param {String} tokenId Unique ID of a non-fungible token
|
||||||
// returns {Boolean} Return whether the burn was successful or not
|
// returns {Boolean} Return whether the burn was successful or not
|
||||||
func (c *TokenERC721Contract) Burn(ctx contractapi.TransactionContextInterface, tokenId string) (bool, error) {
|
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()
|
owner64, err := ctx.GetClientIdentity().GetID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to GetClientIdentity owner64: %v", err)
|
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.
|
// ClientAccountBalance returns the balance of the requesting client's account.
|
||||||
// returns {Number} Returns the account balance
|
// returns {Number} Returns the account balance
|
||||||
func (c *TokenERC721Contract) ClientAccountBalance(ctx contractapi.TransactionContextInterface) (int, error) {
|
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
|
// Get ID of submitting client identity
|
||||||
clientAccountID64, err := ctx.GetClientIdentity().GetID()
|
clientAccountID64, err := ctx.GetClientIdentity().GetID()
|
||||||
if err != nil {
|
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
|
// 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) {
|
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
|
// Get ID of submitting client identity
|
||||||
clientAccountID64, err := ctx.GetClientIdentity().GetID()
|
clientAccountID64, err := ctx.GetClientIdentity().GetID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -617,3 +774,14 @@ func (c *TokenERC721Contract) ClientAccountID(ctx contractapi.TransactionContext
|
||||||
|
|
||||||
return clientAccount, nil
|
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)
|
assert.Equal(t, "https://example.com/nft101.json", tokenURI)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetOption(t *testing.T) {
|
func TestInitialize(t *testing.T) {
|
||||||
ctx, _ := setupStub()
|
ctx, _ := setupStub()
|
||||||
c := new(TokenERC721Contract)
|
c := new(TokenERC721Contract)
|
||||||
|
|
||||||
option, _ := c.SetOption(ctx, "someName", "someSymbol")
|
option, _ := c.Initialize(ctx, "someName", "someSymbol")
|
||||||
assert.Equal(t, true, option)
|
assert.Equal(t, true, option)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,8 @@ public class ERC721TokenContract implements ContractInterface {
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||||
public long BalanceOf(final Context ctx, final String owner) {
|
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 ChaincodeStub stub = ctx.getStub();
|
||||||
final CompositeKey balanceKey =
|
final CompositeKey balanceKey =
|
||||||
stub.createCompositeKey(ContractConstants.BALANCE.getValue(), owner);
|
stub.createCompositeKey(ContractConstants.BALANCE.getValue(), owner);
|
||||||
|
|
@ -76,6 +78,8 @@ public class ERC721TokenContract implements ContractInterface {
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||||
public String OwnerOf(final Context ctx, final String tokenId) {
|
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);
|
final NFT nft = this._readNft(ctx, tokenId);
|
||||||
if (stringIsNullOrEmpty(nft.getOwner())) {
|
if (stringIsNullOrEmpty(nft.getOwner())) {
|
||||||
final String errorMessage = String.format("No owner is assigned o the token %s", tokenId);
|
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)
|
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||||
public boolean IsApprovedForAll(final Context ctx, final String owner, final String operator) {
|
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 ChaincodeStub stub = ctx.getStub();
|
||||||
final CompositeKey approvalKey =
|
final CompositeKey approvalKey =
|
||||||
stub.createCompositeKey(ContractConstants.APPROVAL.getValue(), owner, operator);
|
stub.createCompositeKey(ContractConstants.APPROVAL.getValue(), owner, operator);
|
||||||
|
|
@ -116,6 +122,8 @@ public class ERC721TokenContract implements ContractInterface {
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||||
public void Approve(final Context ctx, final String operator, final String tokenId) {
|
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 ChaincodeStub stub = ctx.getStub();
|
||||||
final String sender = ctx.getClientIdentity().getId();
|
final String sender = ctx.getClientIdentity().getId();
|
||||||
NFT nft = this._readNft(ctx, tokenId);
|
NFT nft = this._readNft(ctx, tokenId);
|
||||||
|
|
@ -144,6 +152,8 @@ public class ERC721TokenContract implements ContractInterface {
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||||
public void SetApprovalForAll(final Context ctx, final String operator, final boolean approved) {
|
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 String sender = ctx.getClientIdentity().getId();
|
||||||
final ChaincodeStub stub = ctx.getStub();
|
final ChaincodeStub stub = ctx.getStub();
|
||||||
final Approval nftApproval = new Approval(sender, operator, approved);
|
final Approval nftApproval = new Approval(sender, operator, approved);
|
||||||
|
|
@ -163,6 +173,8 @@ public class ERC721TokenContract implements ContractInterface {
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||||
public String GetApproved(final Context ctx, final String tokenId) {
|
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);
|
final NFT nft = this._readNft(ctx, tokenId);
|
||||||
return nft.getApproved();
|
return nft.getApproved();
|
||||||
}
|
}
|
||||||
|
|
@ -178,6 +190,8 @@ public class ERC721TokenContract implements ContractInterface {
|
||||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||||
public void TransferFrom(
|
public void TransferFrom(
|
||||||
final Context ctx, final String from, final String to, final String tokenId) {
|
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 String sender = ctx.getClientIdentity().getId();
|
||||||
final ChaincodeStub stub = ctx.getStub();
|
final ChaincodeStub stub = ctx.getStub();
|
||||||
NFT nft = this._readNft(ctx, tokenId);
|
NFT nft = this._readNft(ctx, tokenId);
|
||||||
|
|
@ -235,6 +249,8 @@ public class ERC721TokenContract implements ContractInterface {
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||||
public String Name(final Context ctx) {
|
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());
|
return ctx.getStub().getStringState(ContractConstants.NAMEKEY.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,6 +262,8 @@ public class ERC721TokenContract implements ContractInterface {
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||||
public String Symbol(final Context ctx) {
|
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());
|
return ctx.getStub().getStringState(ContractConstants.SYMBOLKEY.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -258,6 +276,8 @@ public class ERC721TokenContract implements ContractInterface {
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||||
public String TokenURI(final Context ctx, final String tokenId) {
|
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);
|
final NFT nft = this._readNft(ctx, tokenId);
|
||||||
return nft.getTokenURI();
|
return nft.getTokenURI();
|
||||||
}
|
}
|
||||||
|
|
@ -275,6 +295,8 @@ public class ERC721TokenContract implements ContractInterface {
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||||
public long TotalSupply(final Context ctx) {
|
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 ChaincodeStub stub = ctx.getStub();
|
||||||
final CompositeKey nftKey = stub.createCompositeKey(ContractConstants.NFT.getValue());
|
final CompositeKey nftKey = stub.createCompositeKey(ContractConstants.NFT.getValue());
|
||||||
final QueryResultsIterator<KeyValue> iterator = stub.getStateByPartialCompositeKey(nftKey);
|
final QueryResultsIterator<KeyValue> iterator = stub.getStateByPartialCompositeKey(nftKey);
|
||||||
|
|
@ -297,16 +319,24 @@ public class ERC721TokenContract implements ContractInterface {
|
||||||
* @param symbol The symbol of the token
|
* @param symbol The symbol of the token
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
@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();
|
final String clientMSPID = ctx.getClientIdentity().getMSPID();
|
||||||
// Check minter authorization - this sample assumes Org1 is the issuer with privilege to set the
|
// Check minter authorization - this sample assumes Org1 is the issuer with privilege to set the
|
||||||
// name and symbol
|
// name and symbol
|
||||||
if (!clientMSPID.equalsIgnoreCase(ContractConstants.MINTER_ORG_MSP.getValue())) {
|
if (!clientMSPID.equalsIgnoreCase(ContractConstants.MINTER_ORG_MSP.getValue())) {
|
||||||
throw new ChaincodeException(
|
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.NAMEKEY.getValue(), name);
|
||||||
stub.putStringState(ContractConstants.SYMBOLKEY.getValue(), symbol);
|
stub.putStringState(ContractConstants.SYMBOLKEY.getValue(), symbol);
|
||||||
}
|
}
|
||||||
|
|
@ -321,6 +351,8 @@ public class ERC721TokenContract implements ContractInterface {
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||||
public NFT MintWithTokenURI(final Context ctx, final String tokenId, final String tokenURI) {
|
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 String clientMSPID = ctx.getClientIdentity().getMSPID();
|
||||||
final ChaincodeStub stub = ctx.getStub();
|
final ChaincodeStub stub = ctx.getStub();
|
||||||
// Check minter authorization this sample assumes Org1 is the issuer with privilege to mint a
|
// 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)
|
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||||
public void Burn(final Context ctx, final String tokenId) {
|
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 ChaincodeStub stub = ctx.getStub();
|
||||||
final String owner = ctx.getClientIdentity().getId();
|
final String owner = ctx.getClientIdentity().getId();
|
||||||
// Check if a caller is the owner of the non-fungible token
|
// 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)
|
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||||
public long ClientAccountBalance(final Context ctx) {
|
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());
|
return this.BalanceOf(ctx, ctx.getClientIdentity().getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -403,6 +439,8 @@ public class ERC721TokenContract implements ContractInterface {
|
||||||
*/
|
*/
|
||||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||||
public String ClientAccountID(final Context ctx) {
|
public String ClientAccountID(final Context ctx) {
|
||||||
|
//check contract options are already set first to execute the function
|
||||||
|
this.checkInitialized(ctx);
|
||||||
return ctx.getClientIdentity().getId();
|
return ctx.getClientIdentity().getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -437,4 +475,17 @@ public class ERC721TokenContract implements ContractInterface {
|
||||||
final String nft = stub.getStringState(nftKey.toString());
|
final String nft = stub.getStringState(nftKey.toString());
|
||||||
return ((stringIsNullOrEmpty(nft)) ? false : true);
|
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);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
List<KeyValue> list = new ArrayList<>();
|
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"));
|
||||||
list.add(new MockKeyValue("balance_Alice_101", "\u0000"));
|
list.add(new MockKeyValue("balance_Alice_101", "\u0000"));
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
|
@ -103,6 +104,7 @@ public class ERC721TokenContractTest {
|
||||||
NFT nft = new NFT("101", "Alicd", "http://test.com", "");
|
NFT nft = new NFT("101", "Alicd", "http://test.com", "");
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
CompositeKey ck = mock(CompositeKey.class);
|
CompositeKey ck = mock(CompositeKey.class);
|
||||||
|
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(ck.toString()).thenReturn(ContractConstants.NFT.getValue() + "101");
|
when(ck.toString()).thenReturn(ContractConstants.NFT.getValue() + "101");
|
||||||
when(stub.createCompositeKey(ContractConstants.NFT.getValue(), "101")).thenReturn(ck);
|
when(stub.createCompositeKey(ContractConstants.NFT.getValue(), "101")).thenReturn(ck);
|
||||||
when(stub.getStringState(ck.toString())).thenReturn(nft.toJSONString());
|
when(stub.getStringState(ck.toString())).thenReturn(nft.toJSONString());
|
||||||
|
|
@ -129,6 +131,7 @@ public class ERC721TokenContractTest {
|
||||||
this.stub = mock(ChaincodeStub.class);
|
this.stub = mock(ChaincodeStub.class);
|
||||||
when(this.ctx.getStub()).thenReturn(stub);
|
when(this.ctx.getStub()).thenReturn(stub);
|
||||||
contract = new ERC721TokenContract();
|
contract = new ERC721TokenContract();
|
||||||
|
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||||
CompositeKey ck1 = mock(CompositeKey.class);
|
CompositeKey ck1 = mock(CompositeKey.class);
|
||||||
when(ck1.toString()).thenReturn(ContractConstants.NFT.getValue() + "101");
|
when(ck1.toString()).thenReturn(ContractConstants.NFT.getValue() + "101");
|
||||||
when(this.stub.createCompositeKey(ContractConstants.NFT.getValue(), "101")).thenReturn(ck1);
|
when(this.stub.createCompositeKey(ContractConstants.NFT.getValue(), "101")).thenReturn(ck1);
|
||||||
|
|
@ -256,6 +259,7 @@ public class ERC721TokenContractTest {
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
NFT nft = new NFT("101", "Alice", "http://test.com", "");
|
NFT nft = new NFT("101", "Alice", "http://test.com", "");
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||||
CompositeKey ck = mock(CompositeKey.class);
|
CompositeKey ck = mock(CompositeKey.class);
|
||||||
when(ck.toString()).thenReturn(ContractConstants.NFT.getValue() + "101");
|
when(ck.toString()).thenReturn(ContractConstants.NFT.getValue() + "101");
|
||||||
when(stub.createCompositeKey(ContractConstants.NFT.getValue(), "101")).thenReturn(ck);
|
when(stub.createCompositeKey(ContractConstants.NFT.getValue(), "101")).thenReturn(ck);
|
||||||
|
|
@ -281,6 +285,7 @@ public class ERC721TokenContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||||
ClientIdentity ci = null;
|
ClientIdentity ci = null;
|
||||||
ci = mock(ClientIdentity.class);
|
ci = mock(ClientIdentity.class);
|
||||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||||
|
|
@ -306,6 +311,7 @@ public class ERC721TokenContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||||
ClientIdentity ci = null;
|
ClientIdentity ci = null;
|
||||||
ci = mock(ClientIdentity.class);
|
ci = mock(ClientIdentity.class);
|
||||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||||
|
|
@ -327,6 +333,7 @@ public class ERC721TokenContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||||
ClientIdentity ci = null;
|
ClientIdentity ci = null;
|
||||||
ci = mock(ClientIdentity.class);
|
ci = mock(ClientIdentity.class);
|
||||||
when(ctx.getClientIdentity()).thenReturn(ci);
|
when(ctx.getClientIdentity()).thenReturn(ci);
|
||||||
|
|
@ -362,6 +369,7 @@ public class ERC721TokenContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||||
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ANFT");
|
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ANFT");
|
||||||
ERC721TokenContract contract = new ERC721TokenContract();
|
ERC721TokenContract contract = new ERC721TokenContract();
|
||||||
final String name = contract.Name(ctx);
|
final String name = contract.Name(ctx);
|
||||||
|
|
@ -373,6 +381,7 @@ public class ERC721TokenContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||||
ERC721TokenContract contract = new ERC721TokenContract();
|
ERC721TokenContract contract = new ERC721TokenContract();
|
||||||
final NFT nft = new NFT("101", "Alice", "http://test.com", "Bob");
|
final NFT nft = new NFT("101", "Alice", "http://test.com", "Bob");
|
||||||
CompositeKey ck = mock(CompositeKey.class);
|
CompositeKey ck = mock(CompositeKey.class);
|
||||||
|
|
@ -388,6 +397,7 @@ public class ERC721TokenContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||||
List<KeyValue> list = new ArrayList<>();
|
List<KeyValue> list = new ArrayList<>();
|
||||||
list.add(
|
list.add(
|
||||||
new MockKeyValue(
|
new MockKeyValue(
|
||||||
|
|
@ -417,6 +427,7 @@ public class ERC721TokenContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||||
final NFT nft = new NFT("101", "Alice", "DummyURI", "");
|
final NFT nft = new NFT("101", "Alice", "DummyURI", "");
|
||||||
CompositeKey ck = mock(CompositeKey.class);
|
CompositeKey ck = mock(CompositeKey.class);
|
||||||
when(ck.toString()).thenReturn(ContractConstants.NFT.getValue() + "101");
|
when(ck.toString()).thenReturn(ContractConstants.NFT.getValue() + "101");
|
||||||
|
|
@ -445,6 +456,7 @@ public class ERC721TokenContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||||
final NFT nft = new NFT("101", "Alice", "DummyURI", "");
|
final NFT nft = new NFT("101", "Alice", "DummyURI", "");
|
||||||
CompositeKey ck = mock(CompositeKey.class);
|
CompositeKey ck = mock(CompositeKey.class);
|
||||||
when(ck.toString()).thenReturn(ContractConstants.NFT.getValue() + "101");
|
when(ck.toString()).thenReturn(ContractConstants.NFT.getValue() + "101");
|
||||||
|
|
@ -471,6 +483,7 @@ public class ERC721TokenContractTest {
|
||||||
Context ctx = mock(Context.class);
|
Context ctx = mock(Context.class);
|
||||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||||
when(ctx.getStub()).thenReturn(stub);
|
when(ctx.getStub()).thenReturn(stub);
|
||||||
|
when(stub.getStringState(ContractConstants.NAMEKEY.getValue())).thenReturn("ARBTToken");
|
||||||
final NFT nft = new NFT("101", "Alice", "DummyURI", "");
|
final NFT nft = new NFT("101", "Alice", "DummyURI", "");
|
||||||
CompositeKey ck = mock(CompositeKey.class);
|
CompositeKey ck = mock(CompositeKey.class);
|
||||||
when(ck.toString()).thenReturn(ContractConstants.NFT.getValue() + "101");
|
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
|
* @returns {Number} The number of non-fungible tokens owned by the owner, possibly zero
|
||||||
*/
|
*/
|
||||||
async BalanceOf(ctx, owner) {
|
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.
|
// 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.*
|
// BalanceOf() queries for and counts all records matching balancePrefix.owner.*
|
||||||
const iterator = await ctx.stub.getStateByPartialCompositeKey(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
|
* @returns {String} Return the owner of the non-fungible token
|
||||||
*/
|
*/
|
||||||
async OwnerOf(ctx, tokenId) {
|
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 nft = await this._readNFT(ctx, tokenId);
|
||||||
const owner = nft.owner;
|
const owner = nft.owner;
|
||||||
if (!owner) {
|
if (!owner) {
|
||||||
|
|
@ -67,6 +73,9 @@ class TokenERC721Contract extends Contract {
|
||||||
* @returns {Boolean} Return whether the transfer was successful or not
|
* @returns {Boolean} Return whether the transfer was successful or not
|
||||||
*/
|
*/
|
||||||
async TransferFrom(ctx, from, to, tokenId) {
|
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 sender = ctx.clientIdentity.getID();
|
||||||
|
|
||||||
const nft = await this._readNFT(ctx, tokenId);
|
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
|
* @returns {Boolean} Return whether the approval was successful or not
|
||||||
*/
|
*/
|
||||||
async Approve(ctx, approved, tokenId) {
|
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 sender = ctx.clientIdentity.getID();
|
||||||
|
|
||||||
const nft = await this._readNFT(ctx, tokenId);
|
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
|
* @returns {Boolean} Return whether the approval was successful or not
|
||||||
*/
|
*/
|
||||||
async SetApprovalForAll(ctx, operator, approved) {
|
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 sender = ctx.clientIdentity.getID();
|
||||||
|
|
||||||
const approval = { owner: sender, operator: operator, approved: approved };
|
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
|
* @returns {Object} Return the approved client for this non-fungible token, or null if there is none
|
||||||
*/
|
*/
|
||||||
async GetApproved(ctx, tokenId) {
|
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);
|
const nft = await this._readNFT(ctx, tokenId);
|
||||||
return nft.approved;
|
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
|
* @returns {Boolean} Return true if the operator is an approved operator for the owner, false otherwise
|
||||||
*/
|
*/
|
||||||
async IsApprovedForAll(ctx, owner, operator) {
|
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 approvalKey = ctx.stub.createCompositeKey(approvalPrefix, [owner, operator]);
|
||||||
const approvalBytes = await ctx.stub.getState(approvalKey);
|
const approvalBytes = await ctx.stub.getState(approvalKey);
|
||||||
let approved;
|
let approved;
|
||||||
|
|
@ -209,6 +230,9 @@ class TokenERC721Contract extends Contract {
|
||||||
* @returns {String} Returns the name of the token
|
* @returns {String} Returns the name of the token
|
||||||
*/
|
*/
|
||||||
async Name(ctx) {
|
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);
|
const nameAsBytes = await ctx.stub.getState(nameKey);
|
||||||
return nameAsBytes.toString();
|
return nameAsBytes.toString();
|
||||||
}
|
}
|
||||||
|
|
@ -220,6 +244,9 @@ class TokenERC721Contract extends Contract {
|
||||||
* @returns {String} Returns the symbol of the token
|
* @returns {String} Returns the symbol of the token
|
||||||
*/
|
*/
|
||||||
async Symbol(ctx) {
|
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);
|
const symbolAsBytes = await ctx.stub.getState(symbolKey);
|
||||||
return symbolAsBytes.toString();
|
return symbolAsBytes.toString();
|
||||||
}
|
}
|
||||||
|
|
@ -232,6 +259,9 @@ class TokenERC721Contract extends Contract {
|
||||||
* @returns {String} Returns the URI of the token
|
* @returns {String} Returns the URI of the token
|
||||||
*/
|
*/
|
||||||
async TokenURI(ctx, tokenId) {
|
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);
|
const nft = await this._readNFT(ctx, tokenId);
|
||||||
return nft.tokenURI;
|
return nft.tokenURI;
|
||||||
}
|
}
|
||||||
|
|
@ -246,6 +276,9 @@ class TokenERC721Contract extends Contract {
|
||||||
* where each one of them has an assigned and queryable owner.
|
* where each one of them has an assigned and queryable owner.
|
||||||
*/
|
*/
|
||||||
async TotalSupply(ctx) {
|
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.
|
// 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.*
|
// TotalSupply() queries for and counts all records matching nftPrefix.*
|
||||||
const iterator = await ctx.stub.getStateByPartialCompositeKey(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} name The name of the token
|
||||||
* @param {String} symbol The symbol 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();
|
const clientMSPID = ctx.clientIdentity.getMSPID();
|
||||||
if (clientMSPID !== 'Org1MSP') {
|
if (clientMSPID !== 'Org1MSP') {
|
||||||
throw new Error('client is not authorized to set the name and symbol of the token');
|
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(nameKey, Buffer.from(name));
|
||||||
await ctx.stub.putState(symbolKey, Buffer.from(symbol));
|
await ctx.stub.putState(symbolKey, Buffer.from(symbol));
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -291,6 +330,8 @@ class TokenERC721Contract extends Contract {
|
||||||
* @returns {Object} Return the non-fungible token object
|
* @returns {Object} Return the non-fungible token object
|
||||||
*/
|
*/
|
||||||
async MintWithTokenURI(ctx, tokenId, tokenURI) {
|
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
|
// Check minter authorization - this sample assumes Org1 is the issuer with privilege to mint a new token
|
||||||
const clientMSPID = ctx.clientIdentity.getMSPID();
|
const clientMSPID = ctx.clientIdentity.getMSPID();
|
||||||
|
|
@ -341,6 +382,9 @@ class TokenERC721Contract extends Contract {
|
||||||
* @returns {Boolean} Return whether the burn was successful or not
|
* @returns {Boolean} Return whether the burn was successful or not
|
||||||
*/
|
*/
|
||||||
async Burn(ctx, tokenId) {
|
async Burn(ctx, tokenId) {
|
||||||
|
//check contract options are already set first to execute the function
|
||||||
|
await this.CheckInitialized(ctx);
|
||||||
|
|
||||||
const owner = ctx.clientIdentity.getID();
|
const owner = ctx.clientIdentity.getID();
|
||||||
|
|
||||||
// Check if a caller is the owner of the non-fungible token
|
// 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
|
* @returns {Number} Returns the account balance
|
||||||
*/
|
*/
|
||||||
async ClientAccountBalance(ctx) {
|
async ClientAccountBalance(ctx) {
|
||||||
|
//check contract options are already set first to execute the function
|
||||||
|
await this.CheckInitialized(ctx);
|
||||||
|
|
||||||
// Get ID of submitting client identity
|
// Get ID of submitting client identity
|
||||||
const clientAccountID = ctx.clientIdentity.getID();
|
const clientAccountID = ctx.clientIdentity.getID();
|
||||||
return this.BalanceOf(ctx, clientAccountID);
|
return this.BalanceOf(ctx, clientAccountID);
|
||||||
|
|
@ -397,10 +444,21 @@ class TokenERC721Contract extends Contract {
|
||||||
// In this implementation, the client account ID is the clientId itself.
|
// 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
|
// 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) {
|
async ClientAccountID(ctx) {
|
||||||
|
//check contract options are already set first to execute the function
|
||||||
|
await this.CheckInitialized(ctx);
|
||||||
|
|
||||||
// Get ID of submitting client identity
|
// Get ID of submitting client identity
|
||||||
const clientAccountID = ctx.clientIdentity.getID();
|
const clientAccountID = ctx.clientIdentity.getID();
|
||||||
return clientAccountID;
|
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;
|
module.exports = TokenERC721Contract;
|
||||||
|
|
@ -20,9 +20,23 @@ type UTXO struct {
|
||||||
Amount int `json:"amount"`
|
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
|
// Mint creates a new unspent transaction output (UTXO) owned by the minter
|
||||||
func (s *SmartContract) Mint(ctx contractapi.TransactionContextInterface, amount int) (*UTXO, error) {
|
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
|
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to mint new tokens
|
||||||
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
||||||
if err != nil {
|
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)
|
// Transfer transfers UTXOs containing tokens from client to recipient(s)
|
||||||
func (s *SmartContract) Transfer(ctx contractapi.TransactionContextInterface, utxoInputKeys []string, utxoOutputs []UTXO) ([]UTXO, error) {
|
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
|
// Get ID of submitting client identity
|
||||||
clientID, err := ctx.GetClientIdentity().GetID()
|
clientID, err := ctx.GetClientIdentity().GetID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -100,7 +123,7 @@ func (s *SmartContract) Transfer(ctx contractapi.TransactionContextInterface, ut
|
||||||
Amount: amount,
|
Amount: amount,
|
||||||
}
|
}
|
||||||
|
|
||||||
totalInputAmount += amount
|
totalInputAmount,err = add(totalInputAmount, amount)
|
||||||
utxoInputs[utxoInputKey] = utxoInput
|
utxoInputs[utxoInputKey] = utxoInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,7 +138,7 @@ func (s *SmartContract) Transfer(ctx contractapi.TransactionContextInterface, ut
|
||||||
|
|
||||||
utxoOutputs[i].Key = fmt.Sprintf("%s.%d", txID, i)
|
utxoOutputs[i].Key = fmt.Sprintf("%s.%d", txID, i)
|
||||||
|
|
||||||
totalOutputAmount += utxoOutput.Amount
|
totalOutputAmount,err = add(totalOutputAmount, utxoOutput.Amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate total inputs equals total outputs
|
// 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
|
// ClientUTXOs returns all UTXOs owned by the calling client
|
||||||
func (s *SmartContract) ClientUTXOs(ctx contractapi.TransactionContextInterface) ([]*UTXO, error) {
|
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
|
// Get ID of submitting client identity
|
||||||
clientID, err := ctx.GetClientIdentity().GetID()
|
clientID, err := ctx.GetClientIdentity().GetID()
|
||||||
if err != nil {
|
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
|
// 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) {
|
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
|
// Get ID of submitting client identity
|
||||||
clientID, err := ctx.GetClientIdentity().GetID()
|
clientID, err := ctx.GetClientIdentity().GetID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -219,3 +260,109 @@ func (s *SmartContract) ClientID(ctx contractapi.TransactionContextInterface) (s
|
||||||
|
|
||||||
return clientID, nil
|
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