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))
}