func()

in chaincode/src/bank/transfer.go [26:125]


func (s *BankChaincode) transfer(stub shim.ChaincodeStubInterface, args []string) sc.Response {
	if len(args) != 4 {
		return shim.Error("Incorret number of args. Expecting 4: fromAccount, toBank, toAccount, amount")
	}

	//sorting arguments
	fromAccNum := args[0]
	toBankID := args[1]
	toAccNum := args[2]
	amountAsString := args[3]
	amount, err := decimal.NewFromString(args[3])

	//validate amount is actually a number
	if err != nil {
		return shim.Error("Unable to parse amount: " + amountAsString)
	}

	if amount.LessThanOrEqual(decimal.NewFromFloat(0.0)) {
		return shim.Error("Second argument (Amount to deposit) must be a positive number")
	}

	//get the fromAccount
	fromAccountAsByes := s.queryAccount(stub, []string{fromAccNum}).Payload
	fromAccount := &account{}
	err = json.Unmarshal(fromAccountAsByes, fromAccount)

	if err != nil {
		return shim.Error("Unable to retrieve to account ID from ledger " + err.Error())
	}

	// check if funds are available
	if fromAccount.Balance.Cmp(amount) == -1 {
		return shim.Error("Account has insufficient funds")
	}

	//query our bank to get the ID of the institution
	bankAsBytes, err := stub.GetState("bank")

	if err != nil {
		return shim.Error("Unable to retrieve bank ID from ledger " + err.Error())
	}

	thisBank := &bank{}
	err = json.Unmarshal(bankAsBytes, thisBank)
	if err != nil {
		return shim.Error("Unable to retrieve bank ID from ledger " + err.Error())
	}

	// is this an interbank transfer? check if recipient bank is not this bank
	if toBankID != thisBank.ID {
		return shim.Error("Interbank transfer is not yet implemented, either implement it or use the solution")
	}

	//if not inter bank transfer, perform an intra bank transfer
	toAccountAsByes := s.queryAccount(stub, []string{toAccNum}).Payload
	toAccount := &account{}
	err = json.Unmarshal(toAccountAsByes, toAccount)

	if err != nil {
		return shim.Error("Unable to retrieve to account ID from ledger " + err.Error() + string(toAccountAsByes))
	}

	//check if from and to accounts use the same currency
	var exchangeRate decimal.Decimal
	if fromAccount.Currency == toAccount.Currency {
		exchangeRate = decimal.NewFromFloat(1.0)
	} else {
		// call handler function to invoke Forex chaincode
		exchangeRateAsFloat, err := getCurrencyConversion(stub, thisBank.ForexContract, fromAccount.Currency, toAccount.Currency)

		if err != nil {
			return shim.Error("Unable to perform currency conversion:" + err.Error())
		}

		exchangeRate = decimal.NewFromFloat(exchangeRateAsFloat)
	}

	//update balances
	fromAccount.Balance = fromAccount.Balance.Sub(amount)
	toAccount.Balance = toAccount.Balance.Add(amount.Mul(exchangeRate))

	// write changes to ledger
	fromAccountAsByes, _ = json.Marshal(fromAccount)
	err = stub.PutState(fromAccNum, fromAccountAsByes)
	if err != nil {
		return shim.Error("Error trying to commit account to ledger" + err.Error())
	}

	toAccountAsByes, _ = json.Marshal(toAccount)
	err = stub.PutState(toAccNum, toAccountAsByes)
	if err != nil {
		return shim.Error("Error trying to commit account to ledger" + err.Error())
	}

	//write out an event of the transfer
	event := &transferEvent{FromAccNumber: fromAccount.AccNumber, FromBankID: thisBank.ID, ToBankID: toBankID, ToAccNumber: toAccNum, Amount: amount.String()}
	eventBytes, _ := json.Marshal(event)
	stub.SetEvent("transfer-event", eventBytes)
	return (shim.Success(nil))
}