Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
81fa3b5
who did this
Jul 2, 2023
4b3370d
adding erc20 forward logic
Jul 3, 2023
f5afd88
Fixed post process aborting due to trying to get ERC721 data from ERC…
Jul 3, 2023
65a050f
stashing
Jul 5, 2023
d0d8029
coingecko id lookup using address, enable multiple transactions
Jul 7, 2023
948f4e9
improved error message
Jul 7, 2023
3ec8721
sanitize coingecko checksums
Jul 7, 2023
e63222b
cull bunk address data from coingecko
Jul 7, 2023
4591fb0
added mock cost lookup for String USDc, fixed EVM estimation and exec…
Jul 10, 2023
2c7ef8c
stashing
Jul 11, 2023
4a3a5b6
Fix Coingecko Data Caching
Jul 11, 2023
caf8ca8
Added subnet token proxies quick-fix / hack, fixed a bug where coinge…
Jul 12, 2023
04da2ff
Merge branch 'develop' into task/sean/str-675
auroter Jul 12, 2023
5c0a237
Refactor call to repos.Contract.GetForValidation after resolving merg…
Jul 12, 2023
b853272
fix unit21 error from giving it comma delineated txIds
Jul 13, 2023
7e27be0
Removed TX IDs from approvals from all front-facing stuff (receipt, t…
Jul 13, 2023
d16178e
Don't dereference nil in code which isn't doing anything yet
Jul 13, 2023
abed33a
Cull Approve txIds where needed
Jul 13, 2023
4cebd90
Fixed a bug where Confirming an EVM TX would hang on failure!!!! Fixe…
Jul 13, 2023
cd1dcd6
Fix snake_case_variables
Jul 14, 2023
5bfcb30
Merge branch 'develop' into task/sean/str-695
auroter Jul 14, 2023
c1dfcd5
fix issue from merge
Jul 16, 2023
a11adec
Fix another merge issue
Jul 16, 2023
018b5db
Fixed a bug where max cached gas value was overwritten before returni…
Jul 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions pkg/service/cost.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,19 +107,19 @@ func GetCoingeckoPlatformMapping() (map[uint64]string, map[string]uint64, error)
return map[uint64]string{}, map[string]uint64{}, libcommon.StringError(err)
}

id_to_platform := make(map[uint64]string)
platform_to_id := make(map[string]uint64)
idToPlatform := make(map[uint64]string)
platformToId := make(map[string]uint64)
for _, p := range platforms {
if p.ChainIdentifier != 0 {
id_to_platform[p.ChainIdentifier] = p.Id
platform_to_id[p.Id] = p.ChainIdentifier
idToPlatform[p.ChainIdentifier] = p.Id
platformToId[p.Id] = p.ChainIdentifier
}
}
return id_to_platform, platform_to_id, nil
return idToPlatform, platformToId, nil
}

func GetCoingeckoCoinMapping() (map[string]string, error) {
_, platform_to_id, err := GetCoingeckoPlatformMapping()
_, platformToId, err := GetCoingeckoPlatformMapping()
if err != nil {
return map[string]string{}, libcommon.StringError(err)
}
Expand All @@ -131,22 +131,22 @@ func GetCoingeckoCoinMapping() (map[string]string, error) {
return map[string]string{}, libcommon.StringError(err)
}

coin_key_to_id := make(map[string]string)
coinKeyToId := make(map[string]string)
for _, coin := range coins {
for key, val := range coin.Platforms {
// There's some weird data floating around in here. Ignore it.
if len(val) != 42 || val[:2] != "0x" {
continue
}
newKey := CoinKey{
ChainId: platform_to_id[key],
ChainId: platformToId[key],
Address: common.SanitizeChecksum(val),
}.String()
coin_key_to_id[newKey] = coin.ID
coinKeyToId[newKey] = coin.ID
}
}

return coin_key_to_id, nil
return coinKeyToId, nil
}

// TODO: This logic is being reused, abstract it by templating and refactor
Expand Down
2 changes: 1 addition & 1 deletion pkg/service/cost_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
func TestGetTokenPrices(t *testing.T) {
keyMap, err := GetCoingeckoCoinMapping()
assert.NoError(t, err)
name, ok := keyMap[CoinKey{ChainId: 43114, Address: "0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e"}]
name, ok := keyMap[CoinKey{ChainId: 43114, Address: "0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e"}.String()]
assert.True(t, ok)
assert.Equal(t, "usd-coin", name)
}
43 changes: 18 additions & 25 deletions pkg/service/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,17 +158,22 @@ func (e executor) TxWait(txIds []string) (uint64, error) {
for _, txId := range txIds {
txHash := ethcommon.HexToHash(txId)
receipt := types.Receipt{}
for receipt.Status == 0 {
pending := true
for pending {
pendingReceipt, err := e.geth.TransactionReceipt(context.Background(), txHash)
// TransactionReceipt returns error "not found" while tx is pending
if err != nil && err.Error() != "not found" {
return 0, libcommon.StringError(err)
return totalGasUsed, libcommon.StringError(err)
}
if pendingReceipt != nil {
receipt = *pendingReceipt
pending = false
}
// TODO: Sleep for a few ms to keep the cpu cooler
}
if receipt.Status == 0 {
return totalGasUsed, libcommon.StringError(errors.New("transaction failed"))
}
totalGasUsed += receipt.GasUsed
}
return totalGasUsed, nil
Expand Down Expand Up @@ -380,9 +385,6 @@ func (e executor) GetTokenIds(txIds []string) ([]string, error) {
tokenId := new(big.Int).SetBytes(log.Topics[3].Bytes())
tokenIds = append(tokenIds, tokenId.String())
}
if len(tokenIds) == 0 {
return []string{}, libcommon.StringError(errors.New("no token ids found / no ERC721 transfer events found"))
}
return tokenIds, nil
}

Expand Down Expand Up @@ -410,15 +412,13 @@ func (e executor) ForwardNonFungibleTokens(txIds []string, recipient string) ([]
}
// Filter events where recipient is our hot wallet
toForward := FilterEventData(eventData, []int{2}, []string{hotWallet.String()})
filteredTxIds := []string{}
tokenIds := []string{}
gasUsed := big.NewInt(0)
calls := []ContractCall{}
for _, log := range toForward {
tokenId := new(big.Int).SetBytes(log.Topics[3].Bytes()).String()
call := ContractCall{
CxAddr: log.Address.String(),
CxFunc: "safeTransferFrom(address,address,uint256)",
CxFunc: "transferFrom(address,address,uint256)",
CxParams: []string{
hotWallet.String(),
recipient,
Expand All @@ -430,16 +430,17 @@ func (e executor) ForwardNonFungibleTokens(txIds []string, recipient string) ([]
}
tokenIds = append(tokenIds, tokenId)
calls = append(calls, call)
}

forwardTxIds := []string{}
if len(calls) > 0 {
forwardTxIds, _, err = e.Initiate(calls)
if err != nil {
return txIds, tokenIds, libcommon.StringError(err)
}
}

forwardTxIds, gas, err := e.Initiate(calls)
filteredTxIds = append(filteredTxIds, forwardTxIds...)
gasUsed = gasUsed.Add(gasUsed, gas)

return txIds, tokenIds, nil
return forwardTxIds, tokenIds, nil
}

func (e executor) ForwardTokens(txIds []string, recipient string) ([]string, []string, []string, error) {
Expand All @@ -453,19 +454,16 @@ func (e executor) ForwardTokens(txIds []string, recipient string) ([]string, []s
}
// Filter events where recipient is our hot wallet
toForward := FilterEventData(eventData, []int{2}, []string{hotWallet.String()})
filteredTxIds := []string{}
tokens := []string{}
quantities := []string{}
gasUsed := big.NewInt(0)
calls := []ContractCall{}
for _, log := range toForward {
quantity := new(big.Int).SetBytes(log.Data).String()
token := log.Address.String()
call := ContractCall{
CxAddr: token,
CxFunc: "safeTransferFrom(address,address,uint256)",
CxFunc: "transfer(address,uint256)",
CxParams: []string{
hotWallet.String(),
recipient,
quantity,
},
Expand All @@ -476,20 +474,15 @@ func (e executor) ForwardTokens(txIds []string, recipient string) ([]string, []s
tokens = append(tokens, token)
quantities = append(quantities, quantity)
calls = append(calls, call)
if err != nil {
return txIds, tokens, quantities, libcommon.StringError(err)
}
}

// TODO: use this
forwardTxIds := []string{}
if len(calls) > 0 {
forwardTxIds, gas, err := e.Initiate(calls)
forwardTxIds, _, err = e.Initiate(calls)
if err != nil {
return txIds, tokens, quantities, libcommon.StringError(err)
}
filteredTxIds = append(filteredTxIds, forwardTxIds...)
gasUsed = gasUsed.Add(gasUsed, gas)
}

return txIds, tokens, quantities, nil
return forwardTxIds, tokens, quantities, nil
}
38 changes: 32 additions & 6 deletions pkg/service/quote_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import (

type QuoteCache interface {
CheckUpdateCachedTransactionRequest(request model.TransactionRequest, desiredInterval int64) (recalculate bool, callEstimate CallEstimate, err error)
PutCachedTransactionRequest(request model.TransactionRequest, data CallEstimate) error
PutCachedTransactionRequest(request model.TransactionRequest, data CallEstimate) (CallEstimate, error)
UpdateMaxCachedTrueGas(request model.TransactionRequest, gas uint64) error
}

type quoteCache struct {
Expand Down Expand Up @@ -50,18 +51,43 @@ func (q quoteCache) CheckUpdateCachedTransactionRequest(request model.Transactio
}
}

func (q quoteCache) PutCachedTransactionRequest(request model.TransactionRequest, data CallEstimate) error {
cacheObject := callEstimateCache{
func (q quoteCache) UpdateMaxCachedTrueGas(request model.TransactionRequest, gas uint64) error {
key := tokenizeTransactionRequest(sanitizeTransactionRequest(request))
cacheObject, err := store.GetObjectFromCache[callEstimateCache](q.redis, key)
if cacheObject.Timestamp == 0 || (err == nil && cacheObject.Gas < gas) {
cacheObject.Gas = gas
cacheObject.Timestamp = time.Now().Unix()
err = store.PutObjectInCache(q.redis, key, cacheObject)
}
if err != nil {
return libcommon.StringError(err)
}
return nil
}

func (q quoteCache) PutCachedTransactionRequest(request model.TransactionRequest, data CallEstimate) (CallEstimate, error) {
key := tokenizeTransactionRequest(sanitizeTransactionRequest(request))
cacheObject, err := store.GetObjectFromCache[callEstimateCache](q.redis, key)
if err != nil {
return CallEstimate{}, libcommon.StringError(err)
}
// Never lower the known gas value - this can come from estimation or a real transaction
if data.Gas < cacheObject.Gas {
data.Gas = cacheObject.Gas
}
cacheObject = callEstimateCache{
Timestamp: time.Now().Unix(),
Value: data.Value.String(),
Gas: data.Gas,
Success: data.Success,
}
err := store.PutObjectInCache(q.redis, tokenizeTransactionRequest(sanitizeTransactionRequest(request)), cacheObject)
err = store.PutObjectInCache(q.redis, tokenizeTransactionRequest(sanitizeTransactionRequest(request)), cacheObject)
if err != nil {
return libcommon.StringError(err)
return CallEstimate{}, libcommon.StringError(err)
}
return nil
value := big.NewInt(0)
value.SetString(cacheObject.Value, 10)
return CallEstimate{Value: *value, Gas: cacheObject.Gas, Success: cacheObject.Success}, nil
}

func sanitizeTransactionRequest(request model.TransactionRequest) model.TransactionRequest {
Expand Down
44 changes: 32 additions & 12 deletions pkg/service/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,9 @@ type transactionProcessingData struct {
PaymentId string
recipientWalletId *string
txIds []string
forwardTxIds []string
cumulativeValue *big.Int
trueGas *uint64
trueGas uint64
tokenIds string
tokenQuantities string
}
Expand Down Expand Up @@ -254,7 +255,7 @@ func (t transaction) safetyCheck(ctx context.Context, p transactionProcessingDat
// Update cache if price is too volatile
if errors.Cause(err).Error() == "verifyQuote: price too volatile" {
quoteCache := NewQuoteCache(t.redis)
err = quoteCache.PutCachedTransactionRequest(p.executionRequest.Quote.TransactionRequest, estimateEVM)
_, err = quoteCache.PutCachedTransactionRequest(p.executionRequest.Quote.TransactionRequest, estimateEVM)
if err != nil {
return p, libcommon.StringError(err)
}
Expand Down Expand Up @@ -426,7 +427,7 @@ func (t transaction) postProcess(ctx context.Context, p transactionProcessingDat

// confirm the Tx on the EVM
trueGas, err := confirmTx(executor, p.txIds)
p.trueGas = &trueGas
p.trueGas = trueGas
if err != nil {
log.Err(err).Msg("Failed to confirm transaction")
// TODO: Handle error instead of returning it
Expand All @@ -443,7 +444,7 @@ func (t transaction) postProcess(ctx context.Context, p transactionProcessingDat
// TODO: Handle error instead of returning it
}

// Get the Token IDs which were transferred to the API
// Get the Token IDs which were transferred
tokenIds, err := executor.GetTokenIds(p.txIds)
if err != nil {
log.Err(err).Msg("Failed to get token ids")
Expand All @@ -454,21 +455,28 @@ func (t transaction) postProcess(ctx context.Context, p transactionProcessingDat
// Forward any non fungible tokens received to the user
// TODO: Use the TX ID/s from this in the receipt
// TODO: Find a way to charge for the gas used in this transaction
if err == nil { // There will be an error if no ERC721 transfer events were detected
executor.ForwardNonFungibleTokens(p.txIds, p.executionRequest.Quote.TransactionRequest.UserAddress)
if len(tokenIds) > 0 {
forwardTxIds /*forwardTokenIds*/, _, err := executor.ForwardNonFungibleTokens(p.txIds, p.executionRequest.Quote.TransactionRequest.UserAddress)
if err != nil {
log.Err(err).Msg("Failed to forward non fungible tokens")
}
p.forwardTxIds = append(p.forwardTxIds, forwardTxIds...)
}

// Get the Token quantities which were transferred to the API
// Get the Token quantities which were transferred
tokenQuantities, err := executor.GetTokenQuantities(p.txIds)
if err != nil {
log.Err(err).Msg("Failed to get token quantities")
// TODO: Handle error instead of returning it
}

p.tokenQuantities = strings.Join(tokenQuantities, ",")

if err == nil {
executor.ForwardTokens(p.txIds, p.executionRequest.Quote.TransactionRequest.UserAddress)
if len(tokenQuantities) > 0 {
forwardTxIds /*forwardTokenAddresses*/, _ /*tokenQuantities*/, _, err := executor.ForwardTokens(p.txIds, p.executionRequest.Quote.TransactionRequest.UserAddress)
if err != nil {
log.Err(err).Msg("Failed to forward tokens")
}
p.forwardTxIds = append(p.forwardTxIds, forwardTxIds...)
}

// Cull any TXIDs from Approve(), the user and Unit21 and the receipt don't need them
Expand All @@ -479,10 +487,22 @@ func (t transaction) postProcess(ctx context.Context, p transactionProcessingDat
}

// TODO: Get the final gas total here and cache it to the quote cache. And use it for subsequent quotes.
forwardGas, err := confirmTx(executor, p.forwardTxIds)
if err != nil {
log.Err(err).Msg("Failed to confirm forwarding transactions")
}
p.trueGas += forwardGas

// We can close the executor because we aren't using it after this
executor.Close()

// Cache the gas associated with this transaction
qc := NewQuoteCache(t.redis)
err = qc.UpdateMaxCachedTrueGas(p.executionRequest.Quote.TransactionRequest, p.trueGas)
if err != nil {
log.Err(err).Msg("Failed to update quote true gas cache")
}

// compute profit
// TODO: factor request.processingFeeAsset in the event of crypto-to-usd
profit, err := t.tenderTransaction(ctx, p)
Expand Down Expand Up @@ -607,7 +627,7 @@ func (t transaction) testTransaction(executor Executor, request model.Transactio
return res, 0, CallEstimate{}, libcommon.StringError(err)
}
if useCache {
err = quoteCache.PutCachedTransactionRequest(request, estimateEVM)
estimateEVM, err = quoteCache.PutCachedTransactionRequest(request, estimateEVM)
if err != nil {
return res, 0, CallEstimate{}, libcommon.StringError(err)
}
Expand Down Expand Up @@ -841,7 +861,7 @@ func (t transaction) tenderTransaction(ctx context.Context, p transactionProcess
defer finish()

cost := NewCost(t.redis)
trueWei := big.NewInt(0).Add(p.cumulativeValue, big.NewInt(int64(*p.trueGas)))
trueWei := big.NewInt(0).Add(p.cumulativeValue, big.NewInt(int64(p.trueGas)))
trueEth := common.WeiToEther(trueWei)
trueUSD, err := cost.LookupUSD(trueEth, p.chain.CoingeckoName, p.chain.CoincapName)
if err != nil {
Expand Down