Skip to content
This repository has been archived by the owner on Feb 15, 2024. It is now read-only.

Commit

Permalink
refactor: update workflow for multiple transfers
Browse files Browse the repository at this point in the history
The transfer workflow can now process multiple transactions at once.
  • Loading branch information
CassiusPacheco committed Nov 17, 2022
1 parent 40a02bc commit d0e3372
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 40 deletions.
8 changes: 6 additions & 2 deletions Sources/ImmutableXCore/ImmutableX.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,12 @@ public struct ImmutableX {
starkSigner: StarkSigner
) async throws -> CreateTransferResponse {
try await transferWorkflow.transfer(
token: token,
recipientAddress: recipientAddress,
transfers: [
.init(
token: token,
recipientAddress: recipientAddress
),
],
signer: signer,
starkSigner: starkSigner
)
Expand Down
75 changes: 49 additions & 26 deletions Sources/ImmutableXCore/Workflows/TransferWorkflow.swift
Original file line number Diff line number Diff line change
@@ -1,73 +1,96 @@
import Foundation

public struct TransferData {
public let token: AssetModel
public let recipientAddress: String

public init(token: AssetModel, recipientAddress: String) {
self.token = token
self.recipientAddress = recipientAddress
}
}

class TransferWorkflow {
/// This is a utility function that will chain the necessary calls to transfer a token.
///
/// - Parameters:
/// - token: to be transferred (ETH, ERC20, or ERC721)
/// - recipientAddress: of the wallet that will receive the token
/// - transfers: contains a list of ``TransferData`` with `token` to be transferred (ETH, ERC20, or ERC721)
/// and `recipientAddress` of the wallet that will receive the token
/// - signer: represents the users L1 wallet to get the address
/// - starkSigner: represents the users L2 wallet used to sign and verify the L2 transaction
/// - Returns: ``CreateTransferResponse`` that will provide the transfer id if successful.
/// - Throws: A variation of ``ImmutableXError``
class func transfer(
token: AssetModel,
recipientAddress: String,
transfers: [TransferData],
signer: Signer,
starkSigner: StarkSigner,
transfersAPI: TransfersAPI.Type = TransfersAPI.self
) async throws -> CreateTransferResponse {
let address = try await signer.getAddress()
let response = try await getSignableTransfer(
address: address,
token: token,
recipientAddress: recipientAddress,
transfers: transfers,
api: transfersAPI
)
let signableResponse = try response
.signableResponses
.first
.orThrow(.invalidRequest(reason: "Invalid signable response"))
let starkSignature = try await starkSigner.signMessage(signableResponse.payloadHash)

guard !response.signableResponses.isEmpty else {
throw ImmutableXError.invalidRequest(reason: "Invalid signable response")
}

let starkSignatures = try await getStarkSignatures(
response.signableResponses,
starkSigner: starkSigner
)
let ethSignature = try await signer.signMessage(response.signableMessage)
let signatures = WorkflowSignatures(
ethAddress: address,
ethSignature: ethSignature,
starkSignature: starkSignature
starkSignatures: starkSignatures
)
return try await createTransfer(
response: response,
signableResponse: signableResponse,
signatures: signatures,
api: transfersAPI
)
}

private static func getStarkSignatures(
_ signableResponses: [SignableTransferResponseDetails],
starkSigner: StarkSigner
) async throws -> [String] {
var signatures = [String]()

for response in signableResponses {
let signature = try await starkSigner.signMessage(response.payloadHash)
signatures.append(signature)
}

return signatures
}

private static func getSignableTransfer(
address: String,
token: AssetModel,
recipientAddress: String,
transfers: [TransferData],
api: TransfersAPI.Type
) async throws -> GetSignableTransferResponse {
try await APIErrorMapper.map(caller: "Signable transfer") {
try await api.getSignableTransfer(
getSignableTransferRequestV2: GetSignableTransferRequest(
senderEtherKey: address,
signableRequests: [
signableRequests: transfers.map {
SignableTransferDetails(
amount: token.quantity,
receiver: recipientAddress,
token: token.asSignableToken()
),
]
amount: $0.token.quantity,
receiver: $0.recipientAddress,
token: $0.token.asSignableToken()
)
}
)
)
}
}

private static func createTransfer(
response: GetSignableTransferResponse,
signableResponse: SignableTransferResponseDetails,
signatures: WorkflowSignatures,
api: TransfersAPI.Type
) async throws -> CreateTransferResponse {
Expand All @@ -76,7 +99,7 @@ class TransferWorkflow {
xImxEthAddress: signatures.ethAddress,
xImxEthSignature: signatures.serializedEthSignature,
createTransferRequestV2: CreateTransferRequest(
requests: [
requests: response.signableResponses.enumerated().map { index, signableResponse in
TransferRequest(
amount: signableResponse.amount,
assetId: signableResponse.assetId,
Expand All @@ -85,9 +108,9 @@ class TransferWorkflow {
receiverStarkKey: signableResponse.receiverStarkKey,
receiverVaultId: signableResponse.receiverVaultId,
senderVaultId: signableResponse.senderVaultId,
starkSignature: signatures.starkSignature
),
],
starkSignature: signatures.starkSignatures[index]
)
},
senderStarkKey: response.senderStarkKey
)
)
Expand Down
29 changes: 28 additions & 1 deletion Sources/ImmutableXCore/Workflows/Workflow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,34 @@ import Foundation
struct WorkflowSignatures {
let ethAddress: String
let ethSignature: String
let starkSignature: String
let starkSignatures: [String]

init(
ethAddress: String,
ethSignature: String,
starkSignatures: [String]
) {
precondition(!starkSignatures.isEmpty, "List cannot be empty")
self.ethAddress = ethAddress
self.ethSignature = ethSignature
self.starkSignatures = starkSignatures
}

init(
ethAddress: String,
ethSignature: String,
starkSignature: String
) {
self.init(
ethAddress: ethAddress,
ethSignature: ethSignature,
starkSignatures: [starkSignature]
)
}

var starkSignature: String {
starkSignatures.first!
}

/// Serialized signature to be supplied to the API
var serializedEthSignature: String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ class TransferWorkflowMock: TransferWorkflow {
companion = nil
}

override class func transfer(token: AssetModel, recipientAddress: String, signer: Signer, starkSigner: StarkSigner, transfersAPI: TransfersAPI.Type = TransfersAPI.self) async throws -> CreateTransferResponse {
override class func transfer(
transfers: [TransferData],
signer: Signer,
starkSigner: StarkSigner,
transfersAPI: TransfersAPI.Type = TransfersAPI.self
) async throws -> CreateTransferResponse {
let companion = companion!

if let error = companion.throwableError {
Expand Down
40 changes: 30 additions & 10 deletions Tests/ImmutableXCoreTests/Workflows/TransferWorkflowTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@ final class TransferWorkflowTests: XCTestCase {

func testTransferERC20FlowSuccess() async throws {
let response = try await TransferWorkflow.transfer(
token: erc20Token,
recipientAddress: recipientAddress,
transfers: [
.init(
token: erc20Token,
recipientAddress: recipientAddress
),
],
signer: SignerMock(),
starkSigner: StarkSignerMock(),
transfersAPI: transfersAPI
Expand All @@ -35,8 +39,12 @@ final class TransferWorkflowTests: XCTestCase {

func testTransferETHFlowSuccess() async throws {
let response = try await TransferWorkflow.transfer(
token: ethToken,
recipientAddress: recipientAddress,
transfers: [
.init(
token: ethToken,
recipientAddress: recipientAddress
),
],
signer: SignerMock(),
starkSigner: StarkSignerMock(),
transfersAPI: transfersAPI
Expand All @@ -47,8 +55,12 @@ final class TransferWorkflowTests: XCTestCase {

func testTransferERC721FlowSuccess() async throws {
let response = try await TransferWorkflow.transfer(
token: erc721Token,
recipientAddress: recipientAddress,
transfers: [
.init(
token: erc721Token,
recipientAddress: recipientAddress
),
],
signer: SignerMock(),
starkSigner: StarkSignerMock(),
transfersAPI: transfersAPI
Expand All @@ -64,8 +76,12 @@ final class TransferWorkflowTests: XCTestCase {

await XCTAssertThrowsErrorAsync {
_ = try await TransferWorkflow.transfer(
token: erc721Token,
recipientAddress: recipientAddress,
transfers: [
.init(
token: erc721Token,
recipientAddress: recipientAddress
),
],
signer: SignerMock(),
starkSigner: StarkSignerMock(),
transfersAPI: self.transfersAPI
Expand All @@ -80,8 +96,12 @@ final class TransferWorkflowTests: XCTestCase {

await XCTAssertThrowsErrorAsync {
_ = try await TransferWorkflow.transfer(
token: erc721Token,
recipientAddress: recipientAddress,
transfers: [
.init(
token: erc721Token,
recipientAddress: recipientAddress
),
],
signer: SignerMock(),
starkSigner: StarkSignerMock(),
transfersAPI: self.transfersAPI
Expand Down

0 comments on commit d0e3372

Please sign in to comment.