I have 2 accounts on Testnet, previously I was able to send Flow tokens between accounts using Flow CLI. Now trying to send transaction using this SDK, but transaction fails on validation with error:
[Error Code: 1009] error caused by: 1 error occurred:
* transaction verification failed: [Error Code: 1006] invalid proposal key: public key 0 on account 31bbb88233df243d does not have a valid signature: [Error Code: 1009] invalid envelope key: public key 0 on account 31bbb88233df243d does not have a valid signature: signature is not valid
I'd be grateful If you could point where to dig, because I totally stuck now.
Here's my code in ViewController.swift file:
import UIKit
import Flow
import CryptoKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
flow.configure(chainID: .testnet)
Task {
do {
let aliceAddress = Flow.Address(hex: "0x31bbb88233df243d")
let bobAddress = Flow.Address(hex: "0x8ce1531666464325")
// get Alice account info:
let aliceAccount: Flow.Account = try await flow.getAccountAtLatestBlock(address: aliceAddress)
print("Alice's Balance:\(Double(aliceAccount.balance) / 100000000)")
let bobAccount: Flow.Account = try await flow.getAccountAtLatestBlock(address: bobAddress)
print("Bob's Balance:\(Double(bobAccount.balance) / 100000000)\n")
// Preparing Alice's transaction:
let amountArg = Flow.Argument(type: .ufix64, value: .ufix64(120))
let recipientArg = Flow.Argument(type: .address, value: .address(bobAddress))
let alicePrivateKey = try P256.Signing.PrivateKey(rawRepresentation: "0xd08382ba70946ee82af61147e6f84c69932e9c69baaf0c7177731fa0ddc28477".hexValue)
let signer = ECDSA_P256_Signer(
address: aliceAccount.address,
keyIndex: aliceAccount.keys.first!.index,
privateKey: alicePrivateKey
)
let txID = try await flow.sendTransaction(chainID: .testnet,
signers: [signer],
script: Transactions.tokenTransfer,
agrument: [amountArg, recipientArg],
authorizer: [aliceAddress],
payerAddress: aliceAddress,
proposerKey: Flow.TransactionProposalKey(
address: aliceAddress,
keyIndex: aliceAccount.keys.first!.index,
sequenceNumber: aliceAccount.keys.first!.sequenceNumber
),
blockID: nil)
print("Transaction sent.\nTx ID: \(txID)")
let transactionResult = try await flow.getTransactionResultById(id: txID)
print("Transaction Details:\n\(transactionResult)")
// Check balances for both accounts after transaction:
let aliceUpdated: Flow.Account = try await flow.getAccountAtLatestBlock(address: aliceAddress)
print("Alice Updated Balance:\(Double(aliceUpdated.balance) / 100000000)\n")
let bobUpdated: Flow.Account = try await flow.getAccountAtLatestBlock(address: bobAddress)
print("Bob's Updated Balance:\(Double(bobUpdated.balance) / 100000000)\n")
print("End of sequence.")
} catch let error {
print("Something went wrong")
print(error.localizedDescription)
}
}
}
}
private extension String {
/// Convert hex string to bytes
var hexValue: [UInt8] {
var startIndex = self.startIndex
return (0 ..< count / 2).compactMap { _ in
let endIndex = index(after: startIndex)
defer { startIndex = index(after: endIndex) }
return UInt8(self[startIndex ... endIndex], radix: 16)
}
}
}
There was no example of correct FlowSigner implementation, but I found one in TestCases and modified it a bit:
import CryptoKit
import Foundation
import Flow
import CryptoSwift
struct ECDSA_P256_Signer: FlowSigner {
var address: Flow.Address
var keyIndex: Int
var privateKey: P256.Signing.PrivateKey
init(address: Flow.Address, keyIndex: Int, privateKey: P256.Signing.PrivateKey) {
self.address = address
self.keyIndex = keyIndex
self.privateKey = privateKey
}
func sign(transaction _: Flow.Transaction, signableData: Data) throws -> Data {
do {
let digest = SHA256.hash(data: signableData.sha3(.sha256))
return try privateKey.signature(for: digest).rawRepresentation
} catch {
throw error
}
}
}
Transaction Cadence code:
import Foundation
public struct Transactions {
private static let fungibleTokenContractAddr: String = "0x9a0766d93b6608b7"
private static let flowTokenContractAddr: String = "0x7e60df042a9c0868"
public static let tokenTransfer: String = """
import FungibleToken from \(fungibleTokenContractAddr)
import FlowToken from \(flowTokenContractAddr)
transaction(amount: UFix64, to: Address) {
// The Vault resource that holds the tokens that are being transferred
let sentVault: @FungibleToken.Vault
prepare(signer: AuthAccount) {
// Get a reference to the signer's stored vault
let vaultRef = signer.borrow<&FlowToken.Vault>(from: /storage/flowTokenVault)
?? panic("Could not borrow reference to the owner's Vault!")
// Withdraw tokens from the signer's stored vault
self.sentVault <- vaultRef.withdraw(amount: amount)
}
execute {
// Get the recipient's public account object
let recipient = getAccount(to)
// Get a reference to the recipient's Receiver
let receiverRef = recipient.getCapability(/public/flowTokenReceiver)
.borrow<&FlowToken.Vault{FungibleToken.Receiver}>()
?? panic("Could not borrow receiver reference to the recipient's Vault")
// Deposit the withdrawn tokens in the recipient's receiver
receiverRef.deposit(from: <-self.sentVault)
}
}
"""
}
I have 2 accounts on Testnet, previously I was able to send Flow tokens between accounts using Flow CLI. Now trying to send transaction using this SDK, but transaction fails on validation with error:
Info: https://f.dnz.dev/d6ff9c65feb166505043c420843d5504523f7a784f9b151550933dda6bb169db
I'd be grateful If you could point where to dig, because I totally stuck now.
Here's my code in ViewController.swift file:
There was no example of correct FlowSigner implementation, but I found one in TestCases and modified it a bit:
Transaction Cadence code: