From 8107395415d7688acc00d56299d67a6d17488c4c Mon Sep 17 00:00:00 2001 From: "aleksej.paschenko" Date: Mon, 3 Jun 2024 20:36:49 +0300 Subject: [PATCH] Stonfi swap --- abi/get_methods.go | 98 ++++++++++++++++++++++++++++++++++ abi/schemas/ston-fi.xml | 22 ++++++++ contract/stonfi/stonfi_swap.go | 97 +++++++++++++++++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 contract/stonfi/stonfi_swap.go diff --git a/abi/get_methods.go b/abi/get_methods.go index 840f09bb..0e22d611 100644 --- a/abi/get_methods.go +++ b/abi/get_methods.go @@ -5,6 +5,7 @@ package abi import ( "context" "fmt" + "github.com/tonkeeper/tongo/boc" "github.com/tonkeeper/tongo/tlb" "github.com/tonkeeper/tongo/ton" @@ -23,6 +24,7 @@ var KnownGetMethodsDecoder = map[string][]func(tlb.VmStack) (string, any, error) "get_collection_data": {DecodeGetCollectionDataResult}, "get_domain": {DecodeGetDomainResult}, "get_editor": {DecodeGetEditorResult}, + "get_expected_outputs": {DecodeGetExpectedOutputs_StonfiResult}, "get_full_domain": {DecodeGetFullDomainResult}, "get_jetton_data": {DecodeGetJettonDataResult}, "get_last_clean_time": {DecodeGetLastCleanTimeResult}, @@ -47,6 +49,7 @@ var KnownGetMethodsDecoder = map[string][]func(tlb.VmStack) (string, any, error) "get_order_data": {DecodeGetOrderDataResult}, "get_params": {DecodeGetParams_WhalesNominatorResult}, "get_plugin_list": {DecodeGetPluginListResult}, + "get_pool_address": {DecodeGetPoolAddress_StonfiResult}, "get_pool_data": {DecodeGetPoolData_StonfiResult, DecodeGetPoolData_TfResult}, "get_pool_full_data": {DecodeGetPoolFullDataResult}, "get_pool_status": {DecodeGetPoolStatusResult}, @@ -156,6 +159,7 @@ var resultTypes = []interface{}{ &GetCollectionDataResult{}, &GetDomainResult{}, &GetEditorResult{}, + &GetExpectedOutputs_StonfiResult{}, &GetFullDomainResult{}, &GetJettonDataResult{}, &GetLastCleanTimeResult{}, @@ -180,6 +184,7 @@ var resultTypes = []interface{}{ &GetOrderDataResult{}, &GetParams_WhalesNominatorResult{}, &GetPluginListResult{}, + &GetPoolAddress_StonfiResult{}, &GetPoolData_StonfiResult{}, &GetPoolData_TfResult{}, &GetPoolFullDataResult{}, @@ -672,6 +677,52 @@ func DecodeGetEditorResult(stack tlb.VmStack) (resultType string, resultAny any, return "GetEditorResult", result, err } +type GetExpectedOutputs_StonfiResult struct { + Out tlb.Int257 + ProtocolFeeOut tlb.Int257 + RefFeeOut tlb.Int257 +} + +func GetExpectedOutputs(ctx context.Context, executor Executor, reqAccountID ton.AccountID, amount tlb.Int257, tokenWallet tlb.MsgAddress) (string, any, error) { + stack := tlb.VmStack{} + var ( + val tlb.VmStackValue + err error + ) + val = tlb.VmStackValue{SumType: "VmStkInt", VmStkInt: amount} + stack.Put(val) + val, err = tlb.TlbStructToVmCellSlice(tokenWallet) + if err != nil { + return "", nil, err + } + stack.Put(val) + + // MethodID = 115709 for "get_expected_outputs" method + errCode, stack, err := executor.RunSmcMethodByID(ctx, reqAccountID, 115709, stack) + if err != nil { + return "", nil, err + } + if errCode != 0 && errCode != 1 { + return "", nil, fmt.Errorf("method execution failed with code: %v", errCode) + } + for _, f := range []func(tlb.VmStack) (string, any, error){DecodeGetExpectedOutputs_StonfiResult} { + s, r, err := f(stack) + if err == nil { + return s, r, nil + } + } + return "", nil, fmt.Errorf("can not decode outputs") +} + +func DecodeGetExpectedOutputs_StonfiResult(stack tlb.VmStack) (resultType string, resultAny any, err error) { + if len(stack) != 3 || (stack[0].SumType != "VmStkTinyInt" && stack[0].SumType != "VmStkInt") || (stack[1].SumType != "VmStkTinyInt" && stack[1].SumType != "VmStkInt") || (stack[2].SumType != "VmStkTinyInt" && stack[2].SumType != "VmStkInt") { + return "", nil, fmt.Errorf("invalid stack format") + } + var result GetExpectedOutputs_StonfiResult + err = stack.Unmarshal(&result) + return "GetExpectedOutputs_StonfiResult", result, err +} + type GetFullDomainResult struct { Domain string } @@ -1568,6 +1619,53 @@ func DecodeGetPluginListResult(stack tlb.VmStack) (resultType string, resultAny return "GetPluginListResult", result, err } +type GetPoolAddress_StonfiResult struct { + PoolAddress tlb.MsgAddress +} + +func GetPoolAddress(ctx context.Context, executor Executor, reqAccountID ton.AccountID, token0 tlb.MsgAddress, token1 tlb.MsgAddress) (string, any, error) { + stack := tlb.VmStack{} + var ( + val tlb.VmStackValue + err error + ) + val, err = tlb.TlbStructToVmCellSlice(token0) + if err != nil { + return "", nil, err + } + stack.Put(val) + val, err = tlb.TlbStructToVmCellSlice(token1) + if err != nil { + return "", nil, err + } + stack.Put(val) + + // MethodID = 101789 for "get_pool_address" method + errCode, stack, err := executor.RunSmcMethodByID(ctx, reqAccountID, 101789, stack) + if err != nil { + return "", nil, err + } + if errCode != 0 && errCode != 1 { + return "", nil, fmt.Errorf("method execution failed with code: %v", errCode) + } + for _, f := range []func(tlb.VmStack) (string, any, error){DecodeGetPoolAddress_StonfiResult} { + s, r, err := f(stack) + if err == nil { + return s, r, nil + } + } + return "", nil, fmt.Errorf("can not decode outputs") +} + +func DecodeGetPoolAddress_StonfiResult(stack tlb.VmStack) (resultType string, resultAny any, err error) { + if len(stack) != 1 || (stack[0].SumType != "VmStkSlice") { + return "", nil, fmt.Errorf("invalid stack format") + } + var result GetPoolAddress_StonfiResult + err = stack.Unmarshal(&result) + return "GetPoolAddress_StonfiResult", result, err +} + type GetPoolData_StonfiResult struct { Reserve0 tlb.Int257 Reserve1 tlb.Int257 diff --git a/abi/schemas/ston-fi.xml b/abi/schemas/ston-fi.xml index d0cea722..2b38f2ec 100644 --- a/abi/schemas/ston-fi.xml +++ b/abi/schemas/ston-fi.xml @@ -21,6 +21,28 @@ + + + msgaddress + msgaddress + + + msgaddress + + + + + + int257 + msgaddress + + + int257 + int257 + int257 + + + int257 diff --git a/contract/stonfi/stonfi_swap.go b/contract/stonfi/stonfi_swap.go new file mode 100644 index 00000000..1db03c8c --- /dev/null +++ b/contract/stonfi/stonfi_swap.go @@ -0,0 +1,97 @@ +package stonfi + +import ( + "context" + "math/big" + + "github.com/tonkeeper/tongo/abi" + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/contract/jetton" + "github.com/tonkeeper/tongo/liteapi" + "github.com/tonkeeper/tongo/tlb" + "github.com/tonkeeper/tongo/ton" +) + +// Stonfi creates a swap message. +type Stonfi struct { + cli *liteapi.Client + + router ton.AccountID + master0, token0 ton.AccountID + master1, token1 ton.AccountID +} + +var TestnetRouter = ton.MustParseAccountID("kQBsGx9ArADUrREB34W-ghgsCgBShvfUr4Jvlu-0KGc33a1n") +var MainnetRouter = ton.MustParseAccountID("EQB3ncyBUTjZUA5EnFKR5_EnOMI9V1tTEAAPaiU71gc4TiUt") +var PTON = ton.MustParseAccountID("EQCM3B12QK1e4yZSf8GtBRT0aLMNyEsBc_DhVfRRtOEffLez") + +func NewStonfi(ctx context.Context, cli *liteapi.Client, router, master0, master1 ton.AccountID) (*Stonfi, error) { + j0 := jetton.New(master0, cli) + token0, err := j0.GetJettonWallet(ctx, router) + if err != nil { + return nil, err + } + j1 := jetton.New(master1, cli) + token1, err := j1.GetJettonWallet(ctx, router) + if err != nil { + return nil, err + } + return &Stonfi{ + cli: cli, + router: router, + master0: master0, + token0: token0, + master1: master1, + token1: token1, + }, nil +} + +func (s *Stonfi) EstimateMinOut(ctx context.Context, amount big.Int) (*big.Int, error) { + _, value, err := abi.GetPoolAddress(ctx, s.cli, s.router, s.token0.ToMsgAddress(), s.token1.ToMsgAddress()) + if err != nil { + return nil, err + } + result, ok := value.(abi.GetPoolAddress_StonfiResult) + if !ok { + return nil, err + } + pool, err := ton.AccountIDFromTlb(result.PoolAddress) + if err != nil { + return nil, err + } + _, output, err := abi.GetExpectedOutputs(context.Background(), s.cli, *pool, tlb.Int257(amount), s.token0.ToMsgAddress()) + if err != nil { + return nil, err + } + result2, ok := output.(abi.GetExpectedOutputs_StonfiResult) + if !ok { + return nil, err + } + outputValue := big.Int(result2.Out) + return &outputValue, nil +} + +func (s *Stonfi) MakeSwapMessage(attachedTON tlb.Grams, forwardTONAmount tlb.Grams, jettonAmount big.Int, minOut big.Int, address ton.AccountID) (*jetton.TransferMessage, error) { + payload := abi.StonfiSwapJettonPayload{ + TokenWallet: s.token1.ToMsgAddress(), + MinOut: tlb.VarUInteger16(minOut), + ToAddress: address.ToMsgAddress(), + } + c := boc.NewCell() + if err := c.WriteUint(0x25938561, 32); err != nil { + return nil, err + } + if err := tlb.Marshal(c, payload); err != nil { + return nil, err + } + jettonTransfer := jetton.TransferMessage{ + Sender: address, + Jetton: jetton.New(s.master0, s.cli), + JettonAmount: &jettonAmount, + Destination: s.router, + AttachedTon: attachedTON, + ForwardTonAmount: forwardTONAmount, + ForwardPayload: c, + } + return &jettonTransfer, nil +}