Skip to content

Commit

Permalink
- Extractor: remove amount fetching from approve operation, since the…
Browse files Browse the repository at this point in the history
…y are not used the way suspected

- Extractor: add OBJKT offer and bid to list of supported types
- add more unit tests and stubs
  • Loading branch information
simonmcl committed Mar 20, 2024
1 parent 3e6cbfe commit 8611ceb
Show file tree
Hide file tree
Showing 6 changed files with 477 additions and 18 deletions.
86 changes: 70 additions & 16 deletions Sources/KukaiCoreSwift/Factories/OperationFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -453,13 +453,12 @@ public class OperationFactory {
/**
Extract rpc amount (without decimal info) a tokenId, and the destination from a michelson `approve` value
*/
public static func tokenIdAndAmountFromApproveMichelson(michelson: Any) -> (rpcAmount: String, tokenId: Decimal?, destination: String)? {
public static func tokenIdAndAmountFromApproveMichelson(michelson: Any) -> (tokenId: Decimal?, destination: String)? {
if let michelsonDict = michelson as? [String: Any] {
let rpcAmountString = michelsonDict.michelsonArgsArray()?.michelsonInt(atIndex: 1)
let rpcDestinationString = michelsonDict.michelsonArgsArray()?.michelsonString(atIndex: 0) ?? ""
let rpcDestinationString = michelsonDict.michelsonArgsArray()?.michelsonString(atIndex: 0)

if let str = rpcAmountString {
return (rpcAmount: str, tokenId: nil, destination: rpcDestinationString)
if let dest = rpcDestinationString {
return (tokenId: nil, destination: dest)
} else {
return nil
}
Expand Down Expand Up @@ -521,6 +520,34 @@ public class OperationFactory {
}
}

/**
Extract rpc amount (without decimal info) michelson `offer` value for a OBJKT offer call
*/
public static func tokenAmountFromOfferMichelson(michelson: Any) -> Decimal? {
if let michelsonDict = michelson as? [String: Any] {
let tokenAmount = michelsonDict.michelsonArgsArray()?.michelsonPair(atIndex: 1)?.michelsonArgsArray()?.michelsonPair(atIndex: 1)?.michelsonArgsArray()?.michelsonInt(atIndex: 0)

return Decimal(string: tokenAmount ?? "0") ?? 0

} else {
return nil
}
}

/**
Extract rpc amount (without decimal info) michelson `offer` value for a OBJKT offer call
*/
public static func tokenAmountFromBidMichelson(michelson: Any) -> Decimal? {
if let michelsonDict = michelson as? [String: Any] {
let tokenAmount = michelsonDict.michelsonArgsArray()?.michelsonInt(atIndex: 0)

return Decimal(string: tokenAmount ?? "0") ?? 0

} else {
return nil
}
}

/**
Extract rpc amount (without decimal info) a tokenId, and the destination from a michelson FA1.2 / FA2 transfer payload
*/
Expand Down Expand Up @@ -570,11 +597,15 @@ public class OperationFactory {
if let michelsonDict = michelson as? [String: Any], let entrypoint = michelsonDict["entrypoint"] as? String {
switch entrypoint {
case OperationTransaction.StandardEntrypoint.approve.rawValue:
return tokenIdAndAmountFromApproveMichelson(michelson: michelsonDict["value"] ?? [:])
if let approveResponse = tokenIdAndAmountFromApproveMichelson(michelson: michelsonDict["value"] ?? [:]) {
return (rpcAmount: "0", tokenId: approveResponse.tokenId, destination: approveResponse.destination)
} else {
return nil
}

case OperationTransaction.StandardEntrypoint.updateOperators.rawValue:
if let updateResponse = tokenIdFromUpdateOperatorsMichelson(michelson: michelsonDict["value"] ?? [:]) {
return (rpcAmount: "0", tokenId: updateResponse.tokenId, destination: updateResponse.destination) // Can extract token address + id, but not amount ... cause nobody likes me
return (rpcAmount: "0", tokenId: updateResponse.tokenId, destination: updateResponse.destination)
} else {
return nil
}
Expand All @@ -597,6 +628,22 @@ public class OperationFactory {
} else {
return nil
}

case OperationTransaction.StandardEntrypoint.offer.rawValue: // OBJKT - make offer
if let response = tokenAmountFromOfferMichelson(michelson: michelsonDict["value"] ?? [:]) {
return (rpcAmount: response.description, tokenId: nil, destination: nil) // Can extract amount, but nothing else

} else {
return nil
}

case OperationTransaction.StandardEntrypoint.bid.rawValue: // OBJKT - bid on auction
if let response = tokenAmountFromBidMichelson(michelson: michelsonDict["value"] ?? [:]) {
return (rpcAmount: response.description, tokenId: nil, destination: nil) // Can extract amount, but nothing else

} else {
return nil
}

default:
return nil
Expand All @@ -613,19 +660,26 @@ public class OperationFactory {
*/
public static func firstNonZeroTokenTransferAmount(operations: [Operation]) -> (tokenContract: String, rpcAmount: String, tokenId: Decimal?, destination: String)? {

for (index, op) in operations.enumerated() {
if let opTrans = op as? OperationTransaction, let details = tokenIdAndAmountFromMichelson(michelson: opTrans.parameters ?? [:]) {
var lastTokenIdAndAmountResults: (rpcAmount: String, tokenId: Decimal?, destination: String?)? = nil
var lastTokenAddress: String? = nil

for op in operations {
if let opTrans = op as? OperationTransaction, let details = tokenIdAndAmountFromMichelson(michelson: opTrans.parameters ?? [:]), let entrypoint = (opTrans.parameters?["entrypoint"] as? String) {

if details.rpcAmount == "0" && (opTrans.parameters?["entrypoint"] as? String) != OperationTransaction.StandardEntrypoint.updateOperators.rawValue {
if details.rpcAmount == "0", (entrypoint == OperationTransaction.StandardEntrypoint.approve.rawValue || entrypoint == OperationTransaction.StandardEntrypoint.updateOperators.rawValue) {

// If we have a rpcAmount of zero, and its not an `update_operators`, move on to next value
continue
// If its an `approve` oepration or an `update_operators` hold onto the details for the next run
lastTokenIdAndAmountResults = details
lastTokenAddress = opTrans.destination

} else if details.rpcAmount == "0", operations.count > (index + 1), let executeDetails = tokenIdAndAmountFromMichelson(michelson: (operations[index+1] as? OperationTransaction)?.parameters ?? [:]) {
} else if let lastDetails = lastTokenIdAndAmountResults,
let lastTokenAddress = lastTokenAddress,
lastDetails.rpcAmount == "0",
(entrypoint != OperationTransaction.StandardEntrypoint.approve.rawValue && entrypoint != OperationTransaction.StandardEntrypoint.updateOperators.rawValue),
let knownOpDetails = tokenIdAndAmountFromMichelson(michelson: opTrans.parameters ?? [:]) {

// If its zero, and was update_operators, check to see if parsing the next operation as a 3route execute, returns the missing piece
// If so return a mixture of the 2 values, as update tells us the token, execute tells us how much
return (tokenContract: opTrans.destination, rpcAmount: executeDetails.rpcAmount, tokenId: details.tokenId, destination: details.destination ?? "")
// If we have a previous set of details from an approve or update, check if we can extract something useful from this one to complete the info
return (tokenContract: lastTokenAddress, rpcAmount: knownOpDetails.rpcAmount, tokenId: lastDetails.tokenId, destination: lastDetails.destination ?? "")

} else {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ public class OperationTransaction: Operation {
case investLiquidity
case divestLiquidity
case withdrawProfit
case execute // 3route
case deposit // crunchy - stake
case execute // 3route
case deposit // crunchy - stake
case offer // OBJKT - make offer
case bid // OBJKT - bid on auction
}

enum CodingKeys: String, CodingKey {
Expand Down
42 changes: 42 additions & 0 deletions Tests/KukaiCoreSwiftTests/Factories/OperationFactoryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -676,4 +676,46 @@ class OperationFactoryTests: XCTestCase {
XCTAssert(details1?.tokenId == 0, details1?.tokenId?.description ?? "-")
XCTAssert(details1?.destination == "KT1L1WZgdsfjEyP5T4ZCYVvN5vgzrNbu18kX", details1?.destination ?? "-")
}

func testExtractorsObjktOffer() {
let decoder = JSONDecoder()

let jsonData = MockConstants.jsonStub(fromFilename: "objkt-offer-fa1")
let jsonOperations = (try? decoder.decode([OperationTransaction].self, from: jsonData)) ?? []
XCTAssert(jsonOperations.count > 0)

let details1 = OperationFactory.Extractor.firstNonZeroTokenTransferAmount(operations: jsonOperations)
XCTAssert(details1?.tokenContract == "KT1LN4LPSqTMS7Sd2CJw4bbDGRkMv2t68Fy9", details1?.tokenContract ?? "-")
XCTAssert(details1?.rpcAmount == "478000", details1?.rpcAmount ?? "-")
XCTAssert(details1?.tokenId == nil, details1?.tokenId?.description ?? "-")
XCTAssert(details1?.destination == "KT1Xjap1TwmDR1d8yEd8ErkraAj2mbdMrPZY", details1?.destination ?? "-")
}

func testExtractorsObjktBid() {
let decoder = JSONDecoder()

let jsonData = MockConstants.jsonStub(fromFilename: "objkt-bid")
let jsonOperations = (try? decoder.decode([OperationTransaction].self, from: jsonData)) ?? []
XCTAssert(jsonOperations.count > 0)

let details1 = OperationFactory.Extractor.firstNonZeroTokenTransferAmount(operations: jsonOperations)
XCTAssert(details1?.tokenContract == "KT1TjnZYs5CGLbmV6yuW169P8Pnr9BiVwwjz", details1?.tokenContract ?? "-")
XCTAssert(details1?.rpcAmount == "2004532", details1?.rpcAmount ?? "-")
XCTAssert(details1?.tokenId == nil, details1?.tokenId?.description ?? "-")
XCTAssert(details1?.destination == "KT18iSHoRW1iogamADWwQSDoZa3QkN4izkqj", details1?.destination ?? "-")
}

func testExtractorsObjktBidWithWrap() {
let decoder = JSONDecoder()

let jsonData = MockConstants.jsonStub(fromFilename: "objkt-bid-with-wrap")
let jsonOperations = (try? decoder.decode([OperationTransaction].self, from: jsonData)) ?? []
XCTAssert(jsonOperations.count > 0)

let details1 = OperationFactory.Extractor.firstNonZeroTokenTransferAmount(operations: jsonOperations)
XCTAssert(details1?.tokenContract == "KT1TjnZYs5CGLbmV6yuW169P8Pnr9BiVwwjz", details1?.tokenContract ?? "-")
XCTAssert(details1?.rpcAmount == "2004532", details1?.rpcAmount ?? "-")
XCTAssert(details1?.tokenId == nil, details1?.tokenId?.description ?? "-")
XCTAssert(details1?.destination == "KT18iSHoRW1iogamADWwQSDoZa3QkN4izkqj", details1?.destination ?? "-")
}
}
109 changes: 109 additions & 0 deletions Tests/KukaiCoreSwiftTests/Stubs/objkt-bid-with-wrap.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
[
{
"amount": "201000000",
"counter": "95756302",
"destination": "KT1TjnZYs5CGLbmV6yuW169P8Pnr9BiVwwjz",
"fee": "0",
"gas_limit": "520000",
"kind": "transaction",
"parameters": {
"entrypoint": "wrap",
"value": {
"args": [
{
"string": "tz1bJyyFMMWhwkdKSi7Ud8fimu72yfjNC44j"
}
],
"prim": "Some"
}
},
"source": "tz1bJyyFMMWhwkdKSi7Ud8fimu72yfjNC44j",
"storage_limit": "60000"
},
{
"amount": "0",
"counter": "95756303",
"destination": "KT1TjnZYs5CGLbmV6yuW169P8Pnr9BiVwwjz",
"fee": "0",
"gas_limit": "520000",
"kind": "transaction",
"parameters": {
"entrypoint": "approve",
"value": {
"args": [
{
"string": "KT18iSHoRW1iogamADWwQSDoZa3QkN4izkqj"
},
{
"int": "0"
}
],
"prim": "Pair"
}
},
"source": "tz1bJyyFMMWhwkdKSi7Ud8fimu72yfjNC44j",
"storage_limit": "60000"
},
{
"amount": "0",
"counter": "95756304",
"destination": "KT1TjnZYs5CGLbmV6yuW169P8Pnr9BiVwwjz",
"fee": "0",
"gas_limit": "520000",
"kind": "transaction",
"parameters": {
"entrypoint": "approve",
"value": {
"args": [
{
"string": "KT18iSHoRW1iogamADWwQSDoZa3QkN4izkqj"
},
{
"int": "201000000"
}
],
"prim": "Pair"
}
},
"source": "tz1bJyyFMMWhwkdKSi7Ud8fimu72yfjNC44j",
"storage_limit": "60000"
},
{
"amount": "0",
"counter": "95756305",
"destination": "KT18iSHoRW1iogamADWwQSDoZa3QkN4izkqj",
"fee": "0",
"gas_limit": "520000",
"kind": "transaction",
"parameters": {
"entrypoint": "bid",
"value": {
"args": [
{
"int": "2004532"
},
{
"args": [
{
"int": "201000000"
},
{
"args": [
[],
{
"prim": "None"
}
],
"prim": "Pair"
}
],
"prim": "Pair"
}
],
"prim": "Pair"
}
},
"source": "tz1bJyyFMMWhwkdKSi7Ud8fimu72yfjNC44j",
"storage_limit": "60000"
}
]
88 changes: 88 additions & 0 deletions Tests/KukaiCoreSwiftTests/Stubs/objkt-bid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
[
{
"amount": "0",
"counter": "95756303",
"destination": "KT1TjnZYs5CGLbmV6yuW169P8Pnr9BiVwwjz",
"fee": "0",
"gas_limit": "520000",
"kind": "transaction",
"parameters": {
"entrypoint": "approve",
"value": {
"args": [
{
"string": "KT18iSHoRW1iogamADWwQSDoZa3QkN4izkqj"
},
{
"int": "0"
}
],
"prim": "Pair"
}
},
"source": "tz1bJyyFMMWhwkdKSi7Ud8fimu72yfjNC44j",
"storage_limit": "60000"
},
{
"amount": "0",
"counter": "95756304",
"destination": "KT1TjnZYs5CGLbmV6yuW169P8Pnr9BiVwwjz",
"fee": "0",
"gas_limit": "520000",
"kind": "transaction",
"parameters": {
"entrypoint": "approve",
"value": {
"args": [
{
"string": "KT18iSHoRW1iogamADWwQSDoZa3QkN4izkqj"
},
{
"int": "201000000"
}
],
"prim": "Pair"
}
},
"source": "tz1bJyyFMMWhwkdKSi7Ud8fimu72yfjNC44j",
"storage_limit": "60000"
},
{
"amount": "0",
"counter": "95756305",
"destination": "KT18iSHoRW1iogamADWwQSDoZa3QkN4izkqj",
"fee": "0",
"gas_limit": "520000",
"kind": "transaction",
"parameters": {
"entrypoint": "bid",
"value": {
"args": [
{
"int": "2004532"
},
{
"args": [
{
"int": "201000000"
},
{
"args": [
[],
{
"prim": "None"
}
],
"prim": "Pair"
}
],
"prim": "Pair"
}
],
"prim": "Pair"
}
},
"source": "tz1bJyyFMMWhwkdKSi7Ud8fimu72yfjNC44j",
"storage_limit": "60000"
}
]
Loading

0 comments on commit 8611ceb

Please sign in to comment.