Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Sepolia network; Support starknet_chainId endpoint #137

Merged
merged 11 commits into from
Feb 6, 2024
32 changes: 18 additions & 14 deletions Sources/Starknet/Accounts/StarknetAccount.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,44 +40,48 @@ public class StarknetAccount: StarknetAccountProtocol {
StarknetDeployAccountTransactionV3(signature: signature, l1ResourceBounds: params.resourceBounds.l1Gas, nonce: params.nonce, contractAddressSalt: salt, constructorCalldata: calldata, classHash: classHash, forFeeEstimation: forFeeEstimation)
}

public func signV1(calls: [StarknetCall], params: StarknetInvokeParamsV1, forFeeEstimation: Bool) throws -> StarknetInvokeTransactionV1 {
public func signV1(calls: [StarknetCall], params: StarknetInvokeParamsV1, forFeeEstimation: Bool) async throws -> StarknetInvokeTransactionV1 {
DelevoXDG marked this conversation as resolved.
Show resolved Hide resolved
let calldata = starknetCallsToExecuteCalldata(calls: calls, cairoVersion: cairoVersion)

let transaction = makeInvokeTransactionV1(calldata: calldata, signature: [], params: params, forFeeEstimation: forFeeEstimation)

let hash = StarknetTransactionHashCalculator.computeHash(of: transaction, chainId: provider.starknetChainId)
let chainId = try await provider.getChainId()
let hash = StarknetTransactionHashCalculator.computeHash(of: transaction, chainId: chainId)

let signature = try signer.sign(transactionHash: hash)

return makeInvokeTransactionV1(calldata: calldata, signature: signature, params: params, forFeeEstimation: forFeeEstimation)
}

public func signV3(calls: [StarknetCall], params: StarknetInvokeParamsV3, forFeeEstimation: Bool) throws -> StarknetInvokeTransactionV3 {
public func signV3(calls: [StarknetCall], params: StarknetInvokeParamsV3, forFeeEstimation: Bool) async throws -> StarknetInvokeTransactionV3 {
let calldata = starknetCallsToExecuteCalldata(calls: calls, cairoVersion: cairoVersion)

let transaction = makeInvokeTransactionV3(calldata: calldata, signature: [], params: params, forFeeEstimation: forFeeEstimation)

let hash = StarknetTransactionHashCalculator.computeHash(of: transaction, chainId: provider.starknetChainId)
let chainId = try await provider.getChainId()
let hash = StarknetTransactionHashCalculator.computeHash(of: transaction, chainId: chainId)

let signature = try signer.sign(transactionHash: hash)

return makeInvokeTransactionV3(calldata: calldata, signature: signature, params: params, forFeeEstimation: forFeeEstimation)
}

public func signDeployAccountV1(classHash: Felt, calldata: StarknetCalldata, salt: Felt, params: StarknetDeployAccountParamsV1, forFeeEstimation: Bool) throws -> StarknetDeployAccountTransactionV1 {
public func signDeployAccountV1(classHash: Felt, calldata: StarknetCalldata, salt: Felt, params: StarknetDeployAccountParamsV1, forFeeEstimation: Bool) async throws -> StarknetDeployAccountTransactionV1 {
let transaction = makeDeployAccountTransactionV1(classHash: classHash, salt: salt, calldata: calldata, signature: [], params: params, forFeeEstimation: forFeeEstimation)

let hash = StarknetTransactionHashCalculator.computeHash(of: transaction, chainId: provider.starknetChainId)
let chainId = try await provider.getChainId()
let hash = StarknetTransactionHashCalculator.computeHash(of: transaction, chainId: chainId)

let signature = try signer.sign(transactionHash: hash)

return makeDeployAccountTransactionV1(classHash: classHash, salt: salt, calldata: calldata, signature: signature, params: params, forFeeEstimation: forFeeEstimation)
}

public func signDeployAccountV3(classHash: Felt, calldata: StarknetCalldata, salt: Felt, params: StarknetDeployAccountParamsV3, forFeeEstimation: Bool) throws -> StarknetDeployAccountTransactionV3 {
public func signDeployAccountV3(classHash: Felt, calldata: StarknetCalldata, salt: Felt, params: StarknetDeployAccountParamsV3, forFeeEstimation: Bool) async throws -> StarknetDeployAccountTransactionV3 {
let transaction = makeDeployAccountTransactionV3(classHash: classHash, salt: salt, calldata: calldata, signature: [], params: params, forFeeEstimation: forFeeEstimation)

let hash = StarknetTransactionHashCalculator.computeHash(of: transaction, chainId: provider.starknetChainId)
let chainId = try await provider.getChainId()
let hash = StarknetTransactionHashCalculator.computeHash(of: transaction, chainId: chainId)

let signature = try signer.sign(transactionHash: hash)

Expand All @@ -102,7 +106,7 @@ public class StarknetAccount: StarknetAccountProtocol {
}

let params = StarknetInvokeParamsV1(nonce: nonce, maxFee: maxFee)
let signedTransaction = try signV1(calls: calls, params: params, forFeeEstimation: false)
let signedTransaction = try await signV1(calls: calls, params: params, forFeeEstimation: false)

return try await provider.addInvokeTransaction(signedTransaction)
}
Expand All @@ -125,35 +129,35 @@ public class StarknetAccount: StarknetAccountProtocol {
}

let params = StarknetInvokeParamsV3(nonce: nonce, l1ResourceBounds: resourceBounds.l1Gas)
let signedTransaction = try signV3(calls: calls, params: params, forFeeEstimation: false)
let signedTransaction = try await signV3(calls: calls, params: params, forFeeEstimation: false)

return try await provider.addInvokeTransaction(signedTransaction)
}

public func estimateFeeV1(calls: [StarknetCall], nonce: Felt, skipValidate: Bool) async throws -> StarknetFeeEstimate {
let params = StarknetInvokeParamsV1(nonce: nonce, maxFee: .zero)
let signedTransaction = try signV1(calls: calls, params: params, forFeeEstimation: true)
let signedTransaction = try await signV1(calls: calls, params: params, forFeeEstimation: true)

return try await provider.estimateFee(for: signedTransaction, simulationFlags: skipValidate ? [.skipValidate] : [])
}

public func estimateFeeV3(calls: [StarknetCall], nonce: Felt, skipValidate: Bool) async throws -> StarknetFeeEstimate {
let params = StarknetInvokeParamsV3(nonce: nonce, l1ResourceBounds: .zero)
let signedTransaction = try signV3(calls: calls, params: params, forFeeEstimation: true)
let signedTransaction = try await signV3(calls: calls, params: params, forFeeEstimation: true)

return try await provider.estimateFee(for: signedTransaction, simulationFlags: skipValidate ? [.skipValidate] : [])
}

public func estimateDeployAccountFeeV1(classHash: Felt, calldata: StarknetCalldata, salt: Felt, nonce: Felt, skipValidate: Bool) async throws -> StarknetFeeEstimate {
let params = StarknetDeployAccountParamsV1(nonce: nonce, maxFee: 0)
let signedTransaction = try signDeployAccountV1(classHash: classHash, calldata: calldata, salt: salt, params: params, forFeeEstimation: true)
let signedTransaction = try await signDeployAccountV1(classHash: classHash, calldata: calldata, salt: salt, params: params, forFeeEstimation: true)

return try await provider.estimateFee(for: signedTransaction, simulationFlags: skipValidate ? [.skipValidate] : [])
}

public func estimateDeployAccountFeeV3(classHash: Felt, calldata: StarknetCalldata, salt: Felt, nonce: Felt, skipValidate: Bool) async throws -> StarknetFeeEstimate {
let params = StarknetDeployAccountParamsV3(nonce: nonce, l1ResourceBounds: .zero)
let signedTransaction = try signDeployAccountV3(classHash: classHash, calldata: calldata, salt: salt, params: params, forFeeEstimation: true)
let signedTransaction = try await signDeployAccountV3(classHash: classHash, calldata: calldata, salt: salt, params: params, forFeeEstimation: true)

return try await provider.estimateFee(for: signedTransaction, simulationFlags: skipValidate ? [.skipValidate] : [])
}
Expand Down
34 changes: 17 additions & 17 deletions Sources/Starknet/Accounts/StarknetAccountProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public protocol StarknetAccountProtocol {
/// - forFeeEstimation: Flag indicating whether the different version of transaction should be used; such transaction can only be used for fee estimation
///
/// - Returns: Signed invoke v1 transaction
func signV1(calls: [StarknetCall], params: StarknetInvokeParamsV1, forFeeEstimation: Bool) throws -> StarknetInvokeTransactionV1
func signV1(calls: [StarknetCall], params: StarknetInvokeParamsV1, forFeeEstimation: Bool) async throws -> StarknetInvokeTransactionV1

/// Sign list of calls as invoke transaction v3
///
Expand All @@ -22,7 +22,7 @@ public protocol StarknetAccountProtocol {
/// - forFeeEstimation: Flag indicating whether the different version of transaction should be used; such transaction can only be used for fee estimation
///
/// - Returns: Signed invoke v3 transaction
func signV3(calls: [StarknetCall], params: StarknetInvokeParamsV3, forFeeEstimation: Bool) throws -> StarknetInvokeTransactionV3
func signV3(calls: [StarknetCall], params: StarknetInvokeParamsV3, forFeeEstimation: Bool) async throws -> StarknetInvokeTransactionV3

/// Create and sign deploy account transaction v1
///
Expand All @@ -34,7 +34,7 @@ public protocol StarknetAccountProtocol {
/// - forFeeEstimation: Flag indicating whether the different version of transaction should be used; such transaction can only be used for fee estimation
///
/// - Returns: Signed deploy account transaction v1
func signDeployAccountV1(classHash: Felt, calldata: StarknetCalldata, salt: Felt, params: StarknetDeployAccountParamsV1, forFeeEstimation: Bool) throws -> StarknetDeployAccountTransactionV1
func signDeployAccountV1(classHash: Felt, calldata: StarknetCalldata, salt: Felt, params: StarknetDeployAccountParamsV1, forFeeEstimation: Bool) async throws -> StarknetDeployAccountTransactionV1

/// Create and sign deploy account transaction v3
///
Expand All @@ -46,15 +46,15 @@ public protocol StarknetAccountProtocol {
/// - forFeeEstimation: Flag indicating whether the different version of transaction should be used; such transaction can only be used for fee estimation
///
/// - Returns: Signed deploy account transaction v3
func signDeployAccountV3(classHash: Felt, calldata: StarknetCalldata, salt: Felt, params: StarknetDeployAccountParamsV3, forFeeEstimation: Bool) throws -> StarknetDeployAccountTransactionV3
func signDeployAccountV3(classHash: Felt, calldata: StarknetCalldata, salt: Felt, params: StarknetDeployAccountParamsV3, forFeeEstimation: Bool) async throws -> StarknetDeployAccountTransactionV3

/// Sign TypedData for off-chain usage with this account's privateKey.
///
/// - Parameters:
/// - typedData: a TypedData object to sign
///
/// - Returns: a signature for provided TypedData object.
func sign(typedData: StarknetTypedData) throws -> StarknetSignature
func sign(typedData: StarknetTypedData) async throws -> StarknetSignature

/// Verify a signature of TypedData on Starknet.
///
Expand Down Expand Up @@ -158,8 +158,8 @@ public extension StarknetAccountProtocol {
/// - params: additional params for a given transaction
///
/// - Returns: Signed invoke transaction v1
func signV1(calls: [StarknetCall], params: StarknetInvokeParamsV1) throws -> StarknetInvokeTransactionV1 {
try signV1(calls: calls, params: params, forFeeEstimation: false)
func signV1(calls: [StarknetCall], params: StarknetInvokeParamsV1) async throws -> StarknetInvokeTransactionV1 {
try await signV1(calls: calls, params: params, forFeeEstimation: false)
}

/// Sign list of calls for execution as invoke transaction v3.
Expand All @@ -170,8 +170,8 @@ public extension StarknetAccountProtocol {
/// - params: additional params for a given transaction
///
/// - Returns: Signed invoke transaction v3
func signV3(calls: [StarknetCall], params: StarknetInvokeParamsV3) throws -> StarknetInvokeTransactionV3 {
try signV3(calls: calls, params: params, forFeeEstimation: false)
func signV3(calls: [StarknetCall], params: StarknetInvokeParamsV3) async throws -> StarknetInvokeTransactionV3 {
try await signV3(calls: calls, params: params, forFeeEstimation: false)
}

/// Create and sign deploy account transaction v1
Expand All @@ -184,8 +184,8 @@ public extension StarknetAccountProtocol {
/// - maxFee: max acceptable fee for the transaction
///
/// - Returns: Signed deploy account transaction v1
func signDeployAccountV1(classHash: Felt, calldata: StarknetCalldata, salt: Felt, maxFee: Felt) throws -> StarknetDeployAccountTransactionV1 {
try signDeployAccountV1(classHash: classHash, calldata: calldata, salt: salt, params: StarknetDeployAccountParamsV1(nonce: .zero, maxFee: maxFee), forFeeEstimation: false)
func signDeployAccountV1(classHash: Felt, calldata: StarknetCalldata, salt: Felt, maxFee: Felt) async throws -> StarknetDeployAccountTransactionV1 {
try await signDeployAccountV1(classHash: classHash, calldata: calldata, salt: salt, params: StarknetDeployAccountParamsV1(nonce: .zero, maxFee: maxFee), forFeeEstimation: false)
}

/// Create and sign deploy account transaction v3
Expand All @@ -198,8 +198,8 @@ public extension StarknetAccountProtocol {
/// - l1ResourceBounds: max acceptable l1 resource bounds
///
/// - Returns: Signed deploy account transaction v3
func signDeployAccountV3(classHash: Felt, calldata: StarknetCalldata, salt: Felt, l1ResourceBounds: StarknetResourceBounds) throws -> StarknetDeployAccountTransactionV3 {
try signDeployAccountV3(classHash: classHash, calldata: calldata, salt: salt, params: StarknetDeployAccountParamsV3(nonce: .zero, l1ResourceBounds: l1ResourceBounds), forFeeEstimation: false)
func signDeployAccountV3(classHash: Felt, calldata: StarknetCalldata, salt: Felt, l1ResourceBounds: StarknetResourceBounds) async throws -> StarknetDeployAccountTransactionV3 {
try await signDeployAccountV3(classHash: classHash, calldata: calldata, salt: salt, params: StarknetDeployAccountParamsV3(nonce: .zero, l1ResourceBounds: l1ResourceBounds), forFeeEstimation: false)
}

/// Sign a call as invoke transaction v1
Expand All @@ -210,8 +210,8 @@ public extension StarknetAccountProtocol {
/// - forFeeEstimation: Flag indicating whether the different version of transaction should be used; such transaction can only be used for fee estimation
///
/// - Returns: Signed invoke transaction v1
func signV1(call: StarknetCall, params: StarknetInvokeParamsV1, forFeeEstimation: Bool = false) throws -> StarknetInvokeTransactionV1 {
try signV1(calls: [call], params: params, forFeeEstimation: forFeeEstimation)
func signV1(call: StarknetCall, params: StarknetInvokeParamsV1, forFeeEstimation: Bool = false) async throws -> StarknetInvokeTransactionV1 {
try await signV1(calls: [call], params: params, forFeeEstimation: forFeeEstimation)
}

/// Sign a call as invoke transaction v3
Expand All @@ -221,8 +221,8 @@ public extension StarknetAccountProtocol {
/// - forFeeEstimation: Flag indicating whether the different version of transaction should be used; such transaction can only be used for fee estimation
///
/// - Returns: Signed invoke transaction v3
func signV3(call: StarknetCall, params: StarknetInvokeParamsV3, forFeeEstimation: Bool = false) throws -> StarknetInvokeTransactionV3 {
try signV3(calls: [call], params: params, forFeeEstimation: forFeeEstimation)
func signV3(call: StarknetCall, params: StarknetInvokeParamsV3, forFeeEstimation: Bool = false) async throws -> StarknetInvokeTransactionV3 {
try await signV3(calls: [call], params: params, forFeeEstimation: forFeeEstimation)
}

/// Execute list of calls as invoke transaction v1
Expand Down
22 changes: 13 additions & 9 deletions Sources/Starknet/Data/StarknetChainId.swift
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import BigInt
import Foundation

public enum StarknetChainId {
case mainnet
case testnet
public enum StarknetChainId: String, Codable, Equatable {
case mainnet = "0x534e5f4d41494e"
case goerli = "0x534e5f474f45524c49"
case sepolia_testnet = "0x534e5f5345504f4c4941"
DelevoXDG marked this conversation as resolved.
Show resolved Hide resolved
case sepolia_integration = "0x534e5f494e544547524154494f4e5f5345504f4c4941"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure we need this tbh.

Copy link
Collaborator Author

@DelevoXDG DelevoXDG Jan 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably won't hurt to have it just in case someone needs it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does goerli integration introduce itself as goerli?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes


public var feltValue: Felt {
switch self {
case .mainnet:
return Felt(fromHex: "0x534e5f4d41494e")!
case .testnet:
return Felt(fromHex: "0x534e5f474f45524c49")!
}
Felt(fromHex: self.rawValue)!
}

enum CodingKeys: String, CodingKey {
case mainnet = "0x534e5f4d41494e"
case goerli = "0x534e5f474f45524c49"
case sepolia_testnet = "0x534e5f5345504f4c4941"
case sepolia_integration = "0x534e5f494e544547524154494f4e5f5345504f4c4941"
}
Copy link
Collaborator Author

@DelevoXDG DelevoXDG Jan 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not actually sure about having StarknetChainId enum, now that we don't have to pass it to the StarknetProvider constructors?

It's more explicit if someone checks chain id, but I'm not sure there are users that don't know what kind of node they're using 😄
Might as well use Felt instead, and avoid the need to make changes in an unlikely event someone needs to use custom chain ids.

WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need it elsewhere? for coding/decoding or for some verification? I suppose chainId endpoint returns hexes, so if we try to communicate about these ids to users somewhere, we might want to use user-readable names

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we only use those to for signing transactions

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that's the case we could probably remove it

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be addressed in #138

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ enum JsonRpcMethod: String, Encodable {
case getTransactionByHash = "starknet_getTransactionByHash"
case getTransactionByBlockIdAndIndex = "starknet_getTransactionByBlockIdAndIndex"
case getTransactionReceipt = "starknet_getTransactionReceipt"
case getTransactionStatus = "starknet_getTransactionStatus"
case getChainId = "starknet_chainId"
case simulateTransactions = "starknet_simulateTransactions"
case estimateMessageFee = "starknet_estimateMessageFee"
case getTransactionStatus = "starknet_getTransactionStatus"
}
24 changes: 14 additions & 10 deletions Sources/Starknet/Providers/StarknetProvider/StarknetProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,31 @@ public enum StarknetProviderError: Error {
}

public class StarknetProvider: StarknetProviderProtocol {
public let starknetChainId: StarknetChainId

private let url: URL
private let networkProvider: HttpNetworkProvider

public init(starknetChainId: StarknetChainId, url: URL) {
self.starknetChainId = starknetChainId
public init(url: URL) {
self.url = url
self.networkProvider = HttpNetworkProvider()
}

public convenience init?(starknetChainId: StarknetChainId, url: String) {
public convenience init?(url: String) {
guard let url = URL(string: url) else {
return nil
}
self.init(starknetChainId: starknetChainId, url: url)
self.init(url: url)
}

public init(starknetChainId: StarknetChainId, url: URL, urlSession: URLSession) {
self.starknetChainId = starknetChainId
public init(url: URL, urlSession: URLSession) {
self.url = url
self.networkProvider = HttpNetworkProvider(session: urlSession)
}

public convenience init?(starknetChainId: StarknetChainId, url: String, urlSession: URLSession) {
public convenience init?(url: String, urlSession: URLSession) {
guard let url = URL(string: url) else {
return nil
}
self.init(starknetChainId: starknetChainId, url: url, urlSession: urlSession)
self.init(url: url, urlSession: urlSession)
}

private func makeRequest<U>(method: JsonRpcMethod, params: some Encodable = EmptyParams(), receive _: U.Type) async throws -> U where U: Decodable {
Expand Down Expand Up @@ -182,6 +178,14 @@ public class StarknetProvider: StarknetProviderProtocol {
return result
}

public func getChainId() async throws -> StarknetChainId {
let params = EmptySequence()

let result = try await makeRequest(method: .getChainId, params: params, receive: StarknetChainId.self)

return result
}

public func simulateTransactions(_ transactions: [any StarknetExecutableTransaction], at blockId: StarknetBlockId, simulationFlags: Set<StarknetSimulationFlag>) async throws -> [StarknetSimulatedTransaction] {
let params = SimulateTransactionsParams(transactions: transactions, blockId: blockId, simulationFlags: simulationFlags)

Expand Down
Loading
Loading