Files
ritmex-bot/docs/lighter/lighter-go-main/sharedlib/sharedlib.go
T

1019 lines
21 KiB
Go

package main
import (
"encoding/json"
"fmt"
"strings"
"time"
"github.com/elliottech/lighter-go/client"
"github.com/elliottech/lighter-go/types"
curve "github.com/elliottech/poseidon_crypto/curve/ecgfp5"
schnorr "github.com/elliottech/poseidon_crypto/signature/schnorr"
"github.com/ethereum/go-ethereum/common/hexutil"
)
/*
#include <stdlib.h>
typedef struct {
char* str;
char* err;
} StrOrErr;
typedef struct {
char* privateKey;
char* publicKey;
char* err;
} ApiKeyResponse;
*/
import "C"
var (
txClient *client.TxClient
backupTxClients map[uint8]*client.TxClient
)
func wrapErr(err error) (ret *C.char) {
return C.CString(fmt.Sprintf("%v", err))
}
//export GenerateAPIKey
func GenerateAPIKey(cSeed *C.char) (ret C.ApiKeyResponse) {
var err error
var privateKeyStr string
var publicKeyStr string
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
if err != nil {
ret = C.ApiKeyResponse{
err: wrapErr(err),
}
} else {
ret = C.ApiKeyResponse{
privateKey: C.CString(privateKeyStr),
publicKey: C.CString(publicKeyStr),
}
}
}()
seed := C.GoString(cSeed)
seedP := &seed
if seed == "" {
seedP = nil
}
key := curve.SampleScalar(seedP)
publicKeyStr = hexutil.Encode(schnorr.SchnorrPkFromSk(key).ToLittleEndianBytes())
privateKeyStr = hexutil.Encode(key.ToLittleEndianBytes())
return
}
//export CreateClient
func CreateClient(cUrl *C.char, cPrivateKey *C.char, cChainId C.int, cApiKeyIndex C.int, cAccountIndex C.longlong) (ret *C.char) {
var err error
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
if err != nil {
ret = wrapErr(err)
}
}()
url := C.GoString(cUrl)
privateKey := C.GoString(cPrivateKey)
chainId := uint32(cChainId)
apiKeyIndex := uint8(cApiKeyIndex)
accountIndex := int64(cAccountIndex)
if accountIndex <= 0 {
err = fmt.Errorf("invalid account index")
return
}
httpClient := client.NewHTTPClient(url)
txClient, err = client.NewTxClient(httpClient, privateKey, accountIndex, apiKeyIndex, chainId)
if err != nil {
err = fmt.Errorf("error occurred when creating TxClient. err: %v", err)
return
}
if backupTxClients == nil {
backupTxClients = make(map[uint8]*client.TxClient)
}
backupTxClients[apiKeyIndex] = txClient
return nil
}
//export CheckClient
func CheckClient(cApiKeyIndex C.int, cAccountIndex C.longlong) (ret *C.char) {
var err error
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
if err != nil {
ret = wrapErr(err)
}
}()
apiKeyIndex := uint8(cApiKeyIndex)
accountIndex := int64(cAccountIndex)
client, ok := backupTxClients[apiKeyIndex]
if !ok {
err = fmt.Errorf("api key not registered")
return
}
if client.GetApiKeyIndex() != apiKeyIndex {
err = fmt.Errorf("apiKeyIndex does not match. expected %v but got %v", client.GetApiKeyIndex(), apiKeyIndex)
return
}
if client.GetAccountIndex() != accountIndex {
err = fmt.Errorf("accountIndex does not match. expected %v but got %v", client.GetAccountIndex(), accountIndex)
return
}
// check that the API key registered on Lighter matches this one
key, err := client.HTTP().GetApiKey(accountIndex, apiKeyIndex)
if err != nil {
err = fmt.Errorf("failed to get Api Keys. err: %v", err)
return
}
pubKeyBytes := client.GetKeyManager().PubKeyBytes()
pubKeyStr := hexutil.Encode(pubKeyBytes[:])
pubKeyStr = strings.Replace(pubKeyStr, "0x", "", 1)
ak := key.ApiKeys[0]
if ak.PublicKey != pubKeyStr {
err = fmt.Errorf("private key does not match the one on Lighter. ownPubKey: %s response: %+v", pubKeyStr, ak)
return
}
return
}
//export SignChangePubKey
func SignChangePubKey(cPubKey *C.char, cNonce C.longlong) (ret C.StrOrErr) {
// Note: The ChangePubKey TX needs to be signed by the API key that's being changed to as well.
// Because of that, there's no reason to add the params for apiKeyIndex & accountIndex, because this
// version of the SDK doesn't have support for multiple signers.
// Even if it'd had, the flow would look something like this:
// - first you select which client you're sending the TX from
// - then we use the ApiKeyIndex & AccountIndex from that client
var err error
var txInfoStr string
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
if err != nil {
ret = C.StrOrErr{
err: wrapErr(err),
}
} else {
ret = C.StrOrErr{
str: C.CString(txInfoStr),
}
}
}()
if txClient == nil {
err = fmt.Errorf("client is not created, call CreateClient() first")
return
}
nonce := int64(cNonce)
// handle PubKey
pubKeyStr := C.GoString(cPubKey)
pubKeyBytes, err := hexutil.Decode(pubKeyStr)
if err != nil {
return
}
if len(pubKeyBytes) != 40 {
err = fmt.Errorf("invalid pub key length. expected 40 but got %v", len(pubKeyBytes))
return
}
var pubKey [40]byte
copy(pubKey[:], pubKeyBytes)
txInfo := &types.ChangePubKeyReq{
PubKey: pubKey,
}
ops := &types.TransactOpts{}
if nonce != -1 {
ops.Nonce = &nonce
}
tx, err := txClient.GetChangePubKeyTransaction(txInfo, ops)
if err != nil {
return
}
// === manually add MessageToSign to the response:
// - marshal the tx
// - unmarshal it into a generic map
// - add the new field
// - marshal it again
txInfoBytes, err := json.Marshal(tx)
if err != nil {
return
}
obj := make(map[string]interface{})
err = json.Unmarshal(txInfoBytes, &obj)
obj["MessageToSign"] = tx.GetL1SignatureBody()
txInfoBytes, err = json.Marshal(obj)
if err != nil {
return
}
txInfoStr = string(txInfoBytes)
return
}
//export SignCreateOrder
func SignCreateOrder(cMarketIndex C.int, cClientOrderIndex C.longlong, cBaseAmount C.longlong, cPrice C.int, cIsAsk C.int, cOrderType C.int, cTimeInForce C.int, cReduceOnly C.int, cTriggerPrice C.int, cOrderExpiry C.longlong, cNonce C.longlong) (ret C.StrOrErr) {
var err error
var txInfoStr string
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
if err != nil {
ret = C.StrOrErr{
err: wrapErr(err),
}
} else {
ret = C.StrOrErr{
str: C.CString(txInfoStr),
}
}
}()
if txClient == nil {
err = fmt.Errorf("client is not created, call CreateClient() first")
return
}
marketIndex := uint8(cMarketIndex)
clientOrderIndex := int64(cClientOrderIndex)
baseAmount := int64(cBaseAmount)
price := uint32(cPrice)
isAsk := uint8(cIsAsk)
orderType := uint8(cOrderType)
timeInForce := uint8(cTimeInForce)
reduceOnly := uint8(cReduceOnly)
triggerPrice := uint32(cTriggerPrice)
orderExpiry := int64(cOrderExpiry)
nonce := int64(cNonce)
if orderExpiry == -1 {
orderExpiry = time.Now().Add(time.Hour * 24 * 28).UnixMilli() // 28 days
}
txInfo := &types.CreateOrderTxReq{
MarketIndex: marketIndex,
ClientOrderIndex: clientOrderIndex,
BaseAmount: baseAmount,
Price: price,
IsAsk: isAsk,
Type: orderType,
TimeInForce: timeInForce,
ReduceOnly: reduceOnly,
TriggerPrice: triggerPrice,
OrderExpiry: orderExpiry,
}
ops := new(types.TransactOpts)
if nonce != -1 {
ops.Nonce = &nonce
}
tx, err := txClient.GetCreateOrderTransaction(txInfo, ops)
if err != nil {
return
}
txInfoBytes, err := json.Marshal(tx)
if err != nil {
return
}
txInfoStr = string(txInfoBytes)
return
}
//export SignCancelOrder
func SignCancelOrder(cMarketIndex C.int, cOrderIndex C.longlong, cNonce C.longlong) (ret C.StrOrErr) {
var err error
var txInfoStr string
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
if err != nil {
ret = C.StrOrErr{
err: wrapErr(err),
}
} else {
ret = C.StrOrErr{
str: C.CString(txInfoStr),
}
}
}()
if txClient == nil {
err = fmt.Errorf("client is not created, call CreateClient() first")
return
}
marketIndex := uint8(cMarketIndex)
orderIndex := int64(cOrderIndex)
nonce := int64(cNonce)
txInfo := &types.CancelOrderTxReq{
MarketIndex: marketIndex,
Index: orderIndex,
}
ops := new(types.TransactOpts)
if nonce != -1 {
ops.Nonce = &nonce
}
tx, err := txClient.GetCancelOrderTransaction(txInfo, ops)
if err != nil {
return
}
txInfoBytes, err := json.Marshal(tx)
if err != nil {
return
}
txInfoStr = string(txInfoBytes)
return
}
//export SignWithdraw
func SignWithdraw(cUSDCAmount C.longlong, cNonce C.longlong) (ret C.StrOrErr) {
var err error
var txInfoStr string
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
if err != nil {
ret = C.StrOrErr{
err: wrapErr(err),
}
} else {
ret = C.StrOrErr{
str: C.CString(txInfoStr),
}
}
}()
if txClient == nil {
err = fmt.Errorf("client is not created, call CreateClient() first")
return
}
usdcAmount := uint64(cUSDCAmount)
nonce := int64(cNonce)
txInfo := types.WithdrawTxReq{
USDCAmount: usdcAmount,
}
ops := new(types.TransactOpts)
if nonce != -1 {
ops.Nonce = &nonce
}
tx, err := txClient.GetWithdrawTransaction(&txInfo, ops)
if err != nil {
return
}
txInfoBytes, err := json.Marshal(tx)
if err != nil {
return
}
txInfoStr = string(txInfoBytes)
return
}
//export SignCreateSubAccount
func SignCreateSubAccount(cNonce C.longlong) (ret C.StrOrErr) {
var err error
var txInfoStr string
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
if err != nil {
ret = C.StrOrErr{
err: wrapErr(err),
}
} else {
ret = C.StrOrErr{
str: C.CString(txInfoStr),
}
}
}()
if txClient == nil {
err = fmt.Errorf("client is not created, call CreateClient() first")
return
}
nonce := int64(cNonce)
ops := new(types.TransactOpts)
if nonce != -1 {
ops.Nonce = &nonce
}
tx, err := txClient.GetCreateSubAccountTransaction(ops)
if err != nil {
return
}
txInfoBytes, err := json.Marshal(tx)
if err != nil {
return
}
txInfoStr = string(txInfoBytes)
return
}
//export SignCancelAllOrders
func SignCancelAllOrders(cTimeInForce C.int, cTime C.longlong, cNonce C.longlong) (ret C.StrOrErr) {
var err error
var txInfoStr string
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
if err != nil {
ret = C.StrOrErr{
err: wrapErr(err),
}
} else {
ret = C.StrOrErr{
str: C.CString(txInfoStr),
}
}
}()
if txClient == nil {
err = fmt.Errorf("client is not created, call CreateClient() first")
return
}
timeInForce := uint8(cTimeInForce)
t := int64(cTime)
nonce := int64(cNonce)
txInfo := &types.CancelAllOrdersTxReq{
TimeInForce: timeInForce,
Time: t,
}
ops := new(types.TransactOpts)
if nonce != -1 {
ops.Nonce = &nonce
}
tx, err := txClient.GetCancelAllOrdersTransaction(txInfo, ops)
if err != nil {
return
}
txInfoBytes, err := json.Marshal(tx)
if err != nil {
return
}
txInfoStr = string(txInfoBytes)
return
}
//export SignModifyOrder
func SignModifyOrder(cMarketIndex C.int, cIndex C.longlong, cBaseAmount C.longlong, cPrice C.longlong, cTriggerPrice C.longlong, cNonce C.longlong) (ret C.StrOrErr) {
var err error
var txInfoStr string
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
if err != nil {
ret = C.StrOrErr{
err: wrapErr(err),
}
} else {
ret = C.StrOrErr{
str: C.CString(txInfoStr),
}
}
}()
if txClient == nil {
err = fmt.Errorf("client is not created, call CreateClient() first")
return
}
marketIndex := uint8(cMarketIndex)
index := int64(cIndex)
baseAmount := int64(cBaseAmount)
price := uint32(cPrice)
triggerPrice := uint32(cTriggerPrice)
nonce := int64(cNonce)
txInfo := &types.ModifyOrderTxReq{
MarketIndex: marketIndex,
Index: index,
BaseAmount: baseAmount,
Price: price,
TriggerPrice: triggerPrice,
}
ops := new(types.TransactOpts)
if nonce != -1 {
ops.Nonce = &nonce
}
tx, err := txClient.GetModifyOrderTransaction(txInfo, ops)
if err != nil {
return
}
txInfoBytes, err := json.Marshal(tx)
if err != nil {
return
}
txInfoStr = string(txInfoBytes)
return
}
//export SignTransfer
func SignTransfer(cToAccountIndex C.longlong, cUSDCAmount C.longlong, cFee C.longlong, cMemo *C.char, cNonce C.longlong) (ret C.StrOrErr) {
var err error
var txInfoStr string
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
if err != nil {
ret = C.StrOrErr{
err: wrapErr(err),
}
} else {
ret = C.StrOrErr{
str: C.CString(txInfoStr),
}
}
}()
if txClient == nil {
err = fmt.Errorf("client is not created, call CreateClient() first")
return
}
toAccountIndex := int64(cToAccountIndex)
usdcAmount := int64(cUSDCAmount)
nonce := int64(cNonce)
fee := int64(cFee)
memo := [32]byte{}
memoStr := C.GoString(cMemo)
if len(memoStr) != 32 {
err = fmt.Errorf("memo expected to be 32 bytes long")
return
}
for i := 0; i < 32; i++ {
memo[i] = byte(memoStr[i])
}
txInfo := &types.TransferTxReq{
ToAccountIndex: toAccountIndex,
USDCAmount: usdcAmount,
Fee: fee,
Memo: memo,
}
ops := new(types.TransactOpts)
if nonce != -1 {
ops.Nonce = &nonce
}
tx, err := txClient.GetTransferTransaction(txInfo, ops)
if err != nil {
return
}
txInfoBytes, err := json.Marshal(tx)
if err != nil {
return
}
txInfoStr = string(txInfoBytes)
obj := make(map[string]interface{})
err = json.Unmarshal(txInfoBytes, &obj)
obj["MessageToSign"] = tx.GetL1SignatureBody()
txInfoBytes, err = json.Marshal(obj)
if err != nil {
return
}
txInfoStr = string(txInfoBytes)
return
}
//export SignCreatePublicPool
func SignCreatePublicPool(cOperatorFee C.longlong, cInitialTotalShares C.longlong, cMinOperatorShareRate C.longlong, cNonce C.longlong) (ret C.StrOrErr) {
var err error
var txInfoStr string
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
if err != nil {
ret = C.StrOrErr{
err: wrapErr(err),
}
} else {
ret = C.StrOrErr{
str: C.CString(txInfoStr),
}
}
}()
if txClient == nil {
err = fmt.Errorf("client is not created, call CreateClient() first")
return
}
operatorFee := int64(cOperatorFee)
initialTotalShares := int64(cInitialTotalShares)
minOperatorShareRate := int64(cMinOperatorShareRate)
nonce := int64(cNonce)
txInfo := &types.CreatePublicPoolTxReq{
OperatorFee: operatorFee,
InitialTotalShares: initialTotalShares,
MinOperatorShareRate: minOperatorShareRate,
}
ops := new(types.TransactOpts)
if nonce != -1 {
ops.Nonce = &nonce
}
tx, err := txClient.GetCreatePublicPoolTransaction(txInfo, ops)
if err != nil {
return
}
txInfoBytes, err := json.Marshal(tx)
if err != nil {
return
}
txInfoStr = string(txInfoBytes)
return
}
//export SignUpdatePublicPool
func SignUpdatePublicPool(cPublicPoolIndex C.longlong, cStatus C.int, cOperatorFee C.longlong, cMinOperatorShareRate C.longlong, cNonce C.longlong) (ret C.StrOrErr) {
var err error
var txInfoStr string
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
if err != nil {
ret = C.StrOrErr{
err: wrapErr(err),
}
} else {
ret = C.StrOrErr{
str: C.CString(txInfoStr),
}
}
}()
if txClient == nil {
err = fmt.Errorf("client is not created, call CreateClient() first")
return
}
publicPoolIndex := int64(cPublicPoolIndex)
status := uint8(cStatus)
operatorFee := int64(cOperatorFee)
minOperatorShareRate := int64(cMinOperatorShareRate)
nonce := int64(cNonce)
txInfo := &types.UpdatePublicPoolTxReq{
PublicPoolIndex: publicPoolIndex,
Status: status,
OperatorFee: operatorFee,
MinOperatorShareRate: minOperatorShareRate,
}
ops := new(types.TransactOpts)
if nonce != -1 {
ops.Nonce = &nonce
}
tx, err := txClient.GetUpdatePublicPoolTransaction(txInfo, ops)
if err != nil {
return
}
txInfoBytes, err := json.Marshal(tx)
if err != nil {
return
}
txInfoStr = string(txInfoBytes)
return
}
//export SignMintShares
func SignMintShares(cPublicPoolIndex C.longlong, cShareAmount C.longlong, cNonce C.longlong) (ret C.StrOrErr) {
var err error
var txInfoStr string
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
if err != nil {
ret = C.StrOrErr{
err: wrapErr(err),
}
} else {
ret = C.StrOrErr{
str: C.CString(txInfoStr),
}
}
}()
if txClient == nil {
err = fmt.Errorf("client is not created, call CreateClient() first")
return
}
publicPoolIndex := int64(cPublicPoolIndex)
shareAmount := int64(cShareAmount)
nonce := int64(cNonce)
txInfo := &types.MintSharesTxReq{
PublicPoolIndex: publicPoolIndex,
ShareAmount: shareAmount,
}
ops := new(types.TransactOpts)
if nonce != -1 {
ops.Nonce = &nonce
}
tx, err := txClient.GetMintSharesTransaction(txInfo, ops)
if err != nil {
return
}
txInfoBytes, err := json.Marshal(tx)
if err != nil {
return
}
txInfoStr = string(txInfoBytes)
return
}
//export SignBurnShares
func SignBurnShares(cPublicPoolIndex C.longlong, cShareAmount C.longlong, cNonce C.longlong) (ret C.StrOrErr) {
var err error
var txInfoStr string
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
if err != nil {
ret = C.StrOrErr{
err: wrapErr(err),
}
} else {
ret = C.StrOrErr{
str: C.CString(txInfoStr),
}
}
}()
if txClient == nil {
err = fmt.Errorf("client is not created, call CreateClient() first")
return
}
publicPoolIndex := int64(cPublicPoolIndex)
shareAmount := int64(cShareAmount)
nonce := int64(cNonce)
txInfo := &types.BurnSharesTxReq{
PublicPoolIndex: publicPoolIndex,
ShareAmount: shareAmount,
}
ops := new(types.TransactOpts)
if nonce != -1 {
ops.Nonce = &nonce
}
tx, err := txClient.GetBurnSharesTransaction(txInfo, ops)
if err != nil {
return
}
txInfoBytes, err := json.Marshal(tx)
if err != nil {
return
}
txInfoStr = string(txInfoBytes)
return
}
//export SignUpdateLeverage
func SignUpdateLeverage(cMarketIndex C.int, cInitialMarginFraction C.int, cMarginMode C.int, cNonce C.longlong) (ret C.StrOrErr) {
var err error
var txInfoStr string
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
if err != nil {
ret = C.StrOrErr{
err: wrapErr(err),
}
} else {
ret = C.StrOrErr{
str: C.CString(txInfoStr),
}
}
}()
if txClient == nil {
err = fmt.Errorf("client is not created, call CreateClient() first")
return
}
marketIndex := uint8(cMarketIndex)
initialMarginFraction := uint16(cInitialMarginFraction)
nonce := int64(cNonce)
marginMode := uint8(cMarginMode)
txInfo := &types.UpdateLeverageTxReq{
MarketIndex: marketIndex,
InitialMarginFraction: initialMarginFraction,
MarginMode: uint8(marginMode),
}
ops := new(types.TransactOpts)
if nonce != -1 {
ops.Nonce = &nonce
}
tx, err := txClient.GetUpdateLeverageTransaction(txInfo, ops)
if err != nil {
return
}
txInfoBytes, err := json.Marshal(tx)
if err != nil {
return
}
txInfoStr = string(txInfoBytes)
return
}
//export CreateAuthToken
func CreateAuthToken(cDeadline C.longlong) (ret C.StrOrErr) {
var err error
var authToken string
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
if err != nil {
ret = C.StrOrErr{
err: wrapErr(err),
}
} else {
ret = C.StrOrErr{
str: C.CString(authToken),
}
}
}()
if txClient == nil {
err = fmt.Errorf("client is not created, call CreateClient() first")
return
}
deadline := int64(cDeadline)
if deadline == 0 {
deadline = time.Now().Add(time.Hour * 7).Unix()
}
authToken, err = txClient.GetAuthToken(time.Unix(deadline, 0))
if err != nil {
return
}
return
}
//export SwitchAPIKey
func SwitchAPIKey(c C.int) (ret *C.char) {
var err error
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
if err != nil {
ret = wrapErr(err)
}
}()
txClient = backupTxClients[uint8(c)]
if txClient == nil {
err = fmt.Errorf("no client initialized for api key")
}
return
}
//export SignUpdateMargin
func SignUpdateMargin(cMarketIndex C.int, cUSDCAmount C.longlong, cDirection C.int, cNonce C.longlong) (ret C.StrOrErr) {
var err error
var txInfoStr string
defer func() {
if r := recover(); r != nil {
wrapErr(fmt.Errorf("panic: %v", r))
}
if err != nil {
ret = C.StrOrErr{
err: wrapErr(err),
}
} else {
ret = C.StrOrErr{
str: C.CString(txInfoStr),
}
}
}()
if txClient == nil {
err = fmt.Errorf("Client is not created, call CreateClient() first")
}
marketIndex := uint8(cMarketIndex)
usdcAmount := int64(cUSDCAmount)
direction := uint8(cDirection)
nonce := int64(cNonce)
txInfo := &types.UpdateMarginTxReq{
MarketIndex: marketIndex,
USDCAmount: usdcAmount,
Direction: direction,
}
ops := new(types.TransactOpts)
if nonce != -1 {
ops.Nonce = &nonce
}
tx, err := txClient.GetUpdateMarginTransaction(txInfo, ops)
txInfoBytes, err := json.Marshal(tx)
txInfoStr = string(txInfoBytes)
return ret
}
func main() {}