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

FEATURE: add QueryClosedOrders() and QueryTrades() for okex #1312

Merged
merged 7 commits into from
Oct 4, 2023

Conversation

MengShue
Copy link

  1. Add QueryClosedOrders() and QueryTrades() and its unit test for OKEX
  2. Merge conflict is expected when FEATURE: add QueryOrderTrades() for okex #1307 merge into main, Will solve conflict when rebasing.

@bbgokarma-bot
Copy link

Welcome back! @MengShue, This pull request may get 795 BBG.

)

//go:generate GetRequest -url "/api/v5/trade/orders-history-archive" -type GetOrderHistoriesRequest -responseDataType .APIResponse
type GetOrderHistoriesRequest struct {
Copy link
Owner

Choose a reason for hiding this comment

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

just noticed that "history" is uncountable, so it should be "OrderHistory" not "OrderHistories"

e := New(key, secret, passphrase)

queryOrder := types.OrderQuery{
Symbol: "BTC-USDT",
Copy link
Owner

Choose a reason for hiding this comment

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

types.OrderQuery is a global type, so the symbol value will be "BTCUSDT" not "BTC-USDT"

Copy link
Author

Choose a reason for hiding this comment

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

Yes, you're right, I use toLocalSymbol to fix it, and I'm afraid my previous PR made the same error, Will fix it in coming PR, already fix QueryTrades(), QueryClosedOrders(), QueryOrder()

e := New(key, secret, passphrase)

queryOrder := types.OrderQuery{
Symbol: "BTC-USDT",
Copy link
Owner

Choose a reason for hiding this comment

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

same here

types.OrderQuery is a global type, so the symbol value will be "BTCUSDT" not "BTC-USDT"

@codecov
Copy link

codecov bot commented Sep 18, 2023

Codecov Report

Merging #1312 (b1c6e01) into main (8f40478) will decrease coverage by 0.06%.
Report is 26 commits behind head on main.
The diff coverage is 3.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1312      +/-   ##
==========================================
- Coverage   20.86%   20.80%   -0.06%     
==========================================
  Files         556      558       +2     
  Lines       39621    39743     +122     
==========================================
+ Hits         8265     8269       +4     
- Misses      30757    30874     +117     
- Partials      599      600       +1     
Files Coverage Δ
pkg/exchange/okex/okexapi/trade.go 0.00% <ø> (ø)
pkg/risk/riskcontrol/circuit_break.go 77.77% <100.00%> (ø)
...ge/okex/okexapi/get_transaction_history_request.go 0.00% <0.00%> (ø)
pkg/exchange/okex/convert.go 0.00% <0.00%> (ø)
...exchange/okex/okexapi/get_order_history_request.go 0.00% <0.00%> (ø)
pkg/exchange/okex/exchange.go 0.00% <0.00%> (ø)

... and 10 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update cf31796...b1c6e01. Read the comment docs.

@bbgokarma-bot
Copy link

Re-estimated karma: this pull request may get 840 BBG


limit := uint64(options.Limit)
if limit > defaultQueryLimit || limit <= 0 {
log.Debugf("limtit is exceeded or zero, update to %d, got: %d", defaultQueryLimit, options.Limit)
Copy link
Owner

Choose a reason for hiding this comment

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

"limit"

Copy link
Owner

Choose a reason for hiding this comment

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

when limit = 0, we should apply default limit

log.Debugf("limtit is exceeded or zero, update to %d, got: %d", defaultQueryLimit, options.Limit)
limit = defaultQueryLimit
}
req.Limit(limit)
Copy link
Owner

Choose a reason for hiding this comment

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

if the limit is optional, you can just apply it only when it's given

Copy link
Author

Choose a reason for hiding this comment

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

Updated

pkg/exchange/okex/exchange.go Outdated Show resolved Hide resolved
// query by time interval
if options.StartTime != nil || options.EndTime != nil {
if options.StartTime != nil {
req.StartTime(options.StartTime.Local())
Copy link
Owner

@c9s c9s Sep 18, 2023

Choose a reason for hiding this comment

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

.Local() means you are converting the time with the local timezone, if it's an unix timestamp field, .Local() does not change the value.

Copy link
Author

Choose a reason for hiding this comment

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

Update, not use .Local()

@bbgokarma-bot
Copy link

Re-estimated karma: this pull request may get 905 BBG

@bbgokarma-bot
Copy link

Re-estimated karma: this pull request may get 952 BBG

@bbgokarma-bot
Copy link

Re-estimated karma: this pull request may get 968 BBG

@MengShue
Copy link
Author

  1. Fix conflict (rebase)
  2. rename histories with history in previous PR QueryOrderTrades()

@@ -32,6 +32,11 @@ const ID = "okex"
// PlatformToken is the platform currency of OKEx, pre-allocate static string here
const PlatformToken = "OKB"

// Constant For query limit
Copy link
Collaborator

Choose a reason for hiding this comment

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

Your comment should be above defaultQueryLimit.

}

/*
Query Closed trades can query closed trades in last 3 months, there are no time interval limitations, as long as until >= since.
Copy link
Collaborator

Choose a reason for hiding this comment

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

In Go, it is idiomatic to start a comment for a function with the function's name. This is especially relevant for exported functions, as the comment will be used to generate documentation. For example, if you have a function named QueryClosedOrders, the comment could start like this: QueryClosedOrders queries the xxx....

Copy link
Collaborator

Choose a reason for hiding this comment

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

query close trades ? or query close orders ?

pkg/exchange/okex/exchange.go Show resolved Hide resolved

/*
Query Closed trades can query closed trades in last 3 months, there are no time interval limitations, as long as until >= since.
Please Use LastTradeID as cursor, only return trades earlier than that trade, that trade is not included.
Copy link
Collaborator

Choose a reason for hiding this comment

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

return trades? or orders?

pkg/exchange/okex/exchange.go Show resolved Hide resolved
req.EndTime(*options.EndTime)
}

res, err := req.Do(ctx)
Copy link
Collaborator

Choose a reason for hiding this comment

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

you can assign response here. like response, err = req.Do(ctx)

}
response = res
} else if options.StartTime == nil && options.EndTime == nil {
res, err := req.Do(ctx)
Copy link
Collaborator

Choose a reason for hiding this comment

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

ditto

}
response = res
} else { // query by trade id
res, err := req.Do(ctx)
Copy link
Collaborator

Choose a reason for hiding this comment

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

ditto

StartTime(since).
EndTime(until).
Limit(defaultQueryLimit).
After(lastOrder).
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we should use Before instead of After. The order ID is generally an incrementing number. Am i right @c9s ?

Copy link
Collaborator

Choose a reason for hiding this comment

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

return nil, fmt.Errorf("failed to call get order histories error: %w", err)
}
for _, trade := range res {
if trade.LastTradeID == lastTradeID {
Copy link
Collaborator

Choose a reason for hiding this comment

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

The last trade id is a cursor, so you should use before and pass lastTradeID. Am i right? @c9s

Copy link
Collaborator

Choose a reason for hiding this comment

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

e.q. req.Before(lastTradeId).Do(ctx)

Copy link
Owner

Choose a reason for hiding this comment

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

sounds like so

@bbgokarma-bot
Copy link

Re-estimated karma: this pull request may get 1158 BBG


/*
QueryClosedOrders can query closed orders in last 3 months, there are no time interval limitations, as long as until >= since.
Please Use lastOrderID as cursor, only return orders earlier than that order, that order is not included.
Copy link
Collaborator

Choose a reason for hiding this comment

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

earlier -> later?

If you want to query orders by time range, please just pass since and until.
If you want to query by cursor, please pass lastOrderID.
Because it get correct response even pass all parameters with right time interval and invalid lastOrderID, like 0.
Time interval boundary unit is second, ex. order created in 1694155903, get response if query until 1694155903, get empty if query until 1694155904
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are the since values the same?

/*
QueryTrades can query trades in last 3 months, there are no time interval limitations, as long as end_time >= start_time.
OKEX do not provide api to query by tradeID, So use /api/v5/trade/orders-history-archive as its official site do.
Please Use LastTradeID as cursor, only return trades earlier than that trade, that trade is not included.
Copy link
Collaborator

Choose a reason for hiding this comment

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

earlier -> later?

}
for _, trade := range res {
if trade.LastTradeID == lastTradeID {
response, err = req.Before(trade.OrderID).Do(ctx)
Copy link
Collaborator

Choose a reason for hiding this comment

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

我的意思是你應該在 L521 就直接設定 before,而不是這裡,這樣撈回來的值就都會是該 order id之後的?
res, err := req.Do(ctx) -> response, err = req.Before(trade.OrderID).Do(ctx)

Copy link
Author

Choose a reason for hiding this comment

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

不能,因為before() after() 裡要的是order id, 我們argument 是trade id, 所以我先撈一筆,找到trade id 是我們要的之後,再用 before( order_id )


func Test_QueryClosedOrders(t *testing.T) {

key, secret, passphrase, ok := testutil.IntegrationTestWithPassphraseConfigured(t, "OKEX")
Copy link
Collaborator

Choose a reason for hiding this comment

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

OKEX -> types.ExchangeOKEx ?

@bbgokarma-bot
Copy link

Re-estimated karma: this pull request may get 1248 BBG

return nil, ErrSymbolRequired
}

if err := tradeRateLimiter.Wait(ctx); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Perhaps we need to adjust token limit to avoid rate limiter error

https://www.okx.com/docs-v5/en/#order-book-trading-trade-get-order-history-last-3-months
Rate Limit: 20 requests per 2 seconds

you declare tradeRateLimiter = rate.NewLimiter(rate.Every(100*time.Millisecond), 5) and used in QueryClosedOrders and QueryTrades

Copy link
Author

Choose a reason for hiding this comment

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

Update, 相當於打了三次,我改成 300 milli seconds, 應該可以

@bbgokarma-bot
Copy link

Re-estimated karma: this pull request may get 1263 BBG

Please Use lastOrderID as cursor, only return orders later than that order, that order is not included.
If you want to query orders by time range, please just pass since and until.
If you want to query by cursor, please pass lastOrderID.
Because it get correct response even pass all parameters with right time interval and invalid lastOrderID, like 0.
Copy link
Owner

Choose a reason for hiding this comment

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

"it gets the correct response"

Copy link
Author

Choose a reason for hiding this comment

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

Update

Please Use lastOrderID as cursor, only return orders later than that order, that order is not included.
If you want to query orders by time range, please just pass since and until.
If you want to query by cursor, please pass lastOrderID.
Because it get correct response even pass all parameters with right time interval and invalid lastOrderID, like 0.
Copy link
Owner

Choose a reason for hiding this comment

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

"even when you pass"

Copy link
Author

Choose a reason for hiding this comment

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

Update

Please Use lastOrderID as cursor, only return orders later than that order, that order is not included.
If you want to query orders by time range, please just pass since and until.
If you want to query by cursor, please pass lastOrderID.
Because it get correct response even pass all parameters with right time interval and invalid lastOrderID, like 0.
Copy link
Owner

Choose a reason for hiding this comment

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

"with the right time"

Copy link
Author

Choose a reason for hiding this comment

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

Update

return nil, fmt.Errorf("query closed order rate limiter wait error: %w", err)
}

var lastOrder string = strconv.FormatUint(lastOrderID, 10)
Copy link
Owner

Choose a reason for hiding this comment

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

lastOrderID could be zero, when it's zero, do you need to pass to the request?

Copy link
Author

Choose a reason for hiding this comment

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

Update

continue
}

if o.Status.Closed() {
Copy link
Owner

Choose a reason for hiding this comment

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

Doesn't the order history request return only closed orders?

Copy link
Author

Choose a reason for hiding this comment

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

Update

@bbgokarma-bot
Copy link

Re-estimated karma: this pull request may get 1357 BBG

var err error
var response []okexapi.OrderDetails
// query by time interval
if options.StartTime != nil || options.EndTime != nil {
Copy link
Owner

Choose a reason for hiding this comment

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

這邊其實應該可以 startTime == nil && endTime == nil 就直接回傳空 slice?

nest 越少層越好

return nil, fmt.Errorf("failed to call get order histories error: %w", err)
}
} else { // query by trade id
lastTradeID := strconv.FormatUint(options.LastTradeID, 10)
Copy link
Owner

Choose a reason for hiding this comment

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

lastTradeID is not used before you send the request, why?

return nil, ErrSymbolRequired
}

req := e.client.NewGetOrderHistoryRequest().InstrumentID(toLocalSymbol(symbol))
Copy link
Owner

Choose a reason for hiding this comment

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

為什麼 QueryTrades 需要先 QueryOrderHistory 呀?

@bbgokarma-bot
Copy link

Re-estimated karma: this pull request may get 1436 BBG

return nil, ErrSymbolRequired
}

if err := tradeRateLimiter.Wait(ctx); err != nil {
Copy link
Owner

Choose a reason for hiding this comment

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

it's an order query, but you're using trade limiter?

Copy link
Author

Choose a reason for hiding this comment

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

delete trade limit, no need to use it.

@bbgokarma-bot
Copy link

Re-estimated karma: this pull request may get 1450 BBG

If you want to query trades by time range, please just pass start_time and end_time.
Because it gets the correct response even when you pass all parameters with the right time interval and invalid LastTradeID, like 0.
*/
func (e *Exchange) QueryTrades(ctx context.Context, symbol string, options *types.TradeQueryOptions) ([]types.Trade, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Perhaps we need to handle query trade with pagination, if you query result greater that 100, the other result will missing?

Copy link
Author

Choose a reason for hiding this comment

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

Already handle, like below

var billID = "" // billId should be emtpy, can't be 0
for {           // pagenation should use "after" (earlier than)
	res, err := req.
		After(billID).
		Do(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to call get order histories error: %w", err)
	}
	response = append(response, res...)
	if len(res) != int(limit) {
		break
	}
	billID = res[limit-1].BillID
}

@bbgokarma-bot
Copy link

Re-estimated karma: this pull request may get 1486 BBG


var err error
var response []okexapi.OrderDetails
Copy link
Owner

Choose a reason for hiding this comment

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

I don't understand, you're calling QueryOrderHistory in the QueryTrades api? is this expected?

Copy link
Author

Choose a reason for hiding this comment

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

Yes ! because OKX didn't provide any api query by trade_id, but QueryOrderHistory has trade_id in its response. So I call QueryOrderHistory to get the same effect.

Copy link
Author

Choose a reason for hiding this comment

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

OKX didn't provide any api query like /my-trade either

Copy link
Collaborator

Choose a reason for hiding this comment

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

你可以測試pagination是否可以動嗎?

Copy link
Author

Choose a reason for hiding this comment

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

you are right, 加了unit test 後發現應該要用 limit 而不是 defaultQueryLimit

req.EndTime(*options.EndTime)
}

var res []okexapi.OrderDetails
Copy link
Collaborator

Choose a reason for hiding this comment

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

rm

Copy link
Author

Choose a reason for hiding this comment

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

updated

Comment on lines 520 to 522
res, err = req.
After(lastOrderID).
Do(ctx)
Copy link
Collaborator

Choose a reason for hiding this comment

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

res, err := req.

Copy link
Author

Choose a reason for hiding this comment

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

updated


var err error
var response []okexapi.OrderDetails
Copy link
Collaborator

Choose a reason for hiding this comment

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

你可以測試pagination是否可以動嗎?

@bbgokarma-bot
Copy link

Re-estimated karma: this pull request may get 1558 BBG

@bbgokarma-bot
Copy link

Re-estimated karma: this pull request may get 1608 BBG

var err error
var response []okexapi.OrderDetails
if options.StartTime == nil && options.EndTime == nil {
return nil, fmt.Errorf("StartTime and EndTime are require parameter!")
Copy link
Collaborator

Choose a reason for hiding this comment

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

required

Copy link
Author

Choose a reason for hiding this comment

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

Updated

req.EndTime(*options.EndTime)
}

var billID = "" // billId should be emtpy, can't be 0
Copy link
Collaborator

Choose a reason for hiding this comment

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

A few questions:

  1. What's the sorting of query result if i take start, end time only?
  2. Wha't the sorting of query result if i take start, end time, after?
  3. What happens when I query for data from three months earlier?

Copy link
Author

Choose a reason for hiding this comment

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

All is descending order.
If you query time period 3 months earlier with start time and end time, will return [] empty slice
But If you query time period 3 months earlier JUST with start time, will return like start with 3 months ago.

Comment on lines 527 to 531
if len(res) == int(limit) {
billID = res[limit-1].BillID
} else {
break
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

It can be simpler:

if len(res) != int(limit) {
break
			}
				billID = res[limit-1].BillID

Copy link
Author

Choose a reason for hiding this comment

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

Updated

Comment on lines 494 to 500
limit := uint64(options.Limit)
if limit > defaultQueryLimit || limit <= 0 {
limit = defaultQueryLimit
log.Debugf("limit is exceeded default limit %d or zero, got: %d, Do not pass limit", defaultQueryLimit, options.Limit)
} else {
req.Limit(limit)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Always pass the limit when making a request. If the server updates the default query limit, your pagination may not work.

How about:

req.Limit(uint64(options.Limit))
if limit > defaultQueryLimit || limit <= 0 {
		req.Limit(defaultQueryLimit)
		
}

Copy link
Author

Choose a reason for hiding this comment

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

Ok. That make sense !


var billID = "" // billId should be emtpy, can't be 0
for { // pagenation should use "after" (earlier than)
res, err := req.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Handle rate limit

Copy link
Author

Choose a reason for hiding this comment

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

I handle it in line 503 - 505

Copy link
Collaborator

Choose a reason for hiding this comment

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

Rate limit的 token應該是每執行1次request就要消耗1個,但你的做法會變成他消耗1個後,有可能發出大於1的requests

這可以下個PR再改

@@ -286,9 +286,10 @@ func toGlobalOrder(okexOrder *okexapi.OrderDetails) (*types.Order, error) {
}

func toGlobalTrade(orderDetail *okexapi.OrderDetails) (*types.Trade, error) {
tradeID, err := strconv.ParseInt(orderDetail.LastTradeID, 10, 64)
// Should use tradeId, but okex use billId to perform pagination, so use billID as tradeID instead.
billID, err := strconv.ParseInt(orderDetail.BillID, 10, 64)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can the bill id be found at okx console?

Copy link
Author

Choose a reason for hiding this comment

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

console? you mean web page? No, You can't find billId in official site, actually you can't find orderId and tradeId either.

@bbgokarma-bot
Copy link

Re-estimated karma: this pull request may get 1756 BBG

@@ -276,6 +276,7 @@ type OrderDetails struct {
LastFilledFee fixedpoint.Value `json:"fillFee"`
LastFilledFeeCurrency string `json:"fillFeeCcy"`
LastFilledPnl fixedpoint.Value `json:"fillPnl"`
BillID string `json:"billId"`
Copy link
Owner

Choose a reason for hiding this comment

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

You can use types.StrInt64 here

Copy link
Author

Choose a reason for hiding this comment

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

ok, will update

}

// test by order id as a cursor
closedOrder, err := e.QueryClosedOrders(context.Background(), string(queryOrder.Symbol), time.Time{}, time.Time{}, 609869603774656544)
Copy link
Owner

Choose a reason for hiding this comment

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

I guess this order ID is your own order and which can work for others

Copy link
Author

Choose a reason for hiding this comment

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

Yes, I will comment out the test

@bbgokarma-bot
Copy link

Re-estimated karma: this pull request may get 1799 BBG

Copy link
Collaborator

@bailantaotao bailantaotao left a comment

Choose a reason for hiding this comment

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

Others lgtm


var billID = "" // billId should be emtpy, can't be 0
for { // pagenation should use "after" (earlier than)
res, err := req.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Rate limit的 token應該是每執行1次request就要消耗1個,但你的做法會變成他消耗1個後,有可能發出大於1的requests

這可以下個PR再改

@c9s c9s merged commit 32b8ca9 into c9s:main Oct 4, 2023
2 of 4 checks passed
@bbgokarma-bot
Copy link

Hi @MengShue,

Well done! 1834 BBG has been sent to your polygon wallet. Please check the following tx:

https://polygonscan.com/tx/0x21ba8c81a8ac1a1b5559248b79b0e0d150727539177c9605c39e9eb44bc633f6

Thank you for your contribution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants