Skip to content

Commit

Permalink
Get state init from GetWalletStateInitAndSalt
Browse files Browse the repository at this point in the history
  • Loading branch information
aleksej-paschenko committed Sep 13, 2024
1 parent fadbb5d commit fcf2d4d
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 40 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ go 1.22.0

require (
github.com/Code-Hex/go-generics-cache v1.5.1
github.com/avast/retry-go v3.0.0+incompatible
github.com/caarlos0/env/v6 v6.10.1
github.com/go-faster/errors v0.7.1
github.com/go-faster/jx v1.1.0
github.com/ogen-go/ogen v1.2.2
github.com/prometheus/client_golang v1.19.1
github.com/rs/cors v1.11.0
github.com/stretchr/testify v1.9.0
github.com/tonkeeper/tongo v1.9.2-0.20240813090128-215bb67f8160
github.com/tonkeeper/tongo v1.9.6-0.20240913095748-e4fe80db484b
go.opentelemetry.io/otel v1.28.0
go.opentelemetry.io/otel/metric v1.28.0
go.opentelemetry.io/otel/trace v1.28.0
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
github.com/Code-Hex/go-generics-cache v1.5.1 h1:6vhZGc5M7Y/YD8cIUcY8kcuQLB4cHR7U+0KMqAA0KcU=
github.com/Code-Hex/go-generics-cache v1.5.1/go.mod h1:qxcC9kRVrct9rHeiYpFWSoW1vxyillCVzX13KZG8dl4=
github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/caarlos0/env/v6 v6.10.1 h1:t1mPSxNpei6M5yAeu1qtRdPAK29Nbcf/n3G7x+b3/II=
Expand Down Expand Up @@ -62,8 +64,8 @@ github.com/snksoft/crc v1.1.0 h1:HkLdI4taFlgGGG1KvsWMpz78PkOC9TkPVpTV/cuWn48=
github.com/snksoft/crc v1.1.0/go.mod h1:5/gUOsgAm7OmIhb6WJzw7w5g2zfJi4FrHYgGPdshE+A=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tonkeeper/tongo v1.9.2-0.20240813090128-215bb67f8160 h1:QW00z3p7ALYKq++3cXrepKEQhPXtS0QcunMTLd60p9w=
github.com/tonkeeper/tongo v1.9.2-0.20240813090128-215bb67f8160/go.mod h1:MjgIgAytFarjCoVjMLjYEtpZNN1f2G/pnZhKjr28cWs=
github.com/tonkeeper/tongo v1.9.6-0.20240913095748-e4fe80db484b h1:5w+JddBpt3Eb6Eam4mwjdR4eC4A3ZwQE7i2eWiDwHyQ=
github.com/tonkeeper/tongo v1.9.6-0.20240913095748-e4fe80db484b/go.mod h1:MjgIgAytFarjCoVjMLjYEtpZNN1f2G/pnZhKjr28cWs=
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
Expand Down
111 changes: 74 additions & 37 deletions pkg/api/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import (
"fmt"
"strconv"
"strings"
"time"

"github.com/avast/retry-go"
"github.com/tonkeeper/tongo/abi"
boc "github.com/tonkeeper/tongo/boc"
"github.com/tonkeeper/tongo/contract/jetton"
"github.com/tonkeeper/tongo/liteapi"
Expand All @@ -24,6 +27,7 @@ type Handler struct {

prover *prover.Prover
jettonMaster ton.AccountID
cli *liteapi.Client
}

type Config struct {
Expand All @@ -38,7 +42,6 @@ func NewHandler(logger *zap.Logger, config Config) (*Handler, error) {
if err != nil {
return nil, err
}
_ = cli
jettonMaster := jetton.New(config.JettonMaster, cli)
_ = jettonMaster

Expand All @@ -51,6 +54,7 @@ func NewHandler(logger *zap.Logger, config Config) (*Handler, error) {
}
return &Handler{
prover: p,
cli: cli,
logger: logger,
jettonMaster: config.JettonMaster,
}, nil
Expand All @@ -69,16 +73,15 @@ func (h *Handler) Run(ctx context.Context) {
go h.prover.Run(ctx)
}

func (h *Handler) convertToWalletInfo(airdrop prover.WalletAirdrop) (*oas.WalletInfo, error) {
func (h *Handler) convertToWalletInfo(ctx context.Context, airdrop prover.WalletAirdrop) (*oas.WalletInfo, error) {
customPayload, err := createCustomPayload(airdrop.Proof)
if err != nil {
return nil, err
}
stateInit, err := createStateInit(airdrop.AccountID, h.jettonMaster, h.prover.MerkleRoot())
stateInit, err := h.getStateInit(ctx, airdrop.AccountID)
if err != nil {
return nil, err
}

compressedInfo := oas.WalletInfoCompressedInfo{
Amount: strconv.FormatUint(uint64(airdrop.Data.Amount), 10),
StartFrom: strconv.FormatUint(uint64(airdrop.Data.StartFrom), 10),
Expand Down Expand Up @@ -112,7 +115,7 @@ func (h *Handler) GetWalletInfo(ctx context.Context, params oas.GetWalletInfoPar
if resp.Err != nil {
return nil, InternalError(resp.Err)
}
info, err := h.convertToWalletInfo(resp.WalletAirdrop)
info, err := h.convertToWalletInfo(ctx, resp.WalletAirdrop)
if err != nil {
return nil, InternalError(err)
}
Expand Down Expand Up @@ -146,38 +149,6 @@ type JettonData struct {
MerkleRoot tlb.Bits256
}

func createStateInit(owner, minter ton.AccountID, merkleRoot tlb.Bits256) (string, error) {
data := JettonData{
Status: 0,
Balance: 0,
OwnerAddress: owner.ToMsgAddress(),
JettonMasterAddress: minter.ToMsgAddress(),
MerkleRoot: merkleRoot,
}

dataCell := boc.NewCell()
if err := tlb.Marshal(dataCell, data); err != nil {
return "", err
}
jettonWalletCodeCells, err := boc.DeserializeBocHex("b5ee9c720101010100230008420259c02d4546e62393684b9ec55ae8b1c9d169415ff94502a93a63b0566c27ba15")
if err != nil {
return "", err
}
if len(jettonWalletCodeCells) != 1 {
return "", fmt.Errorf("unexpected number of cells")
}

state := tlb.StateInit{
Code: tlb.Maybe[tlb.Ref[boc.Cell]]{Exists: true, Value: tlb.Ref[boc.Cell]{Value: *jettonWalletCodeCells[0]}},
Data: tlb.Maybe[tlb.Ref[boc.Cell]]{Exists: true, Value: tlb.Ref[boc.Cell]{Value: *dataCell}},
}
c := boc.NewCell()
if err := tlb.Marshal(c, state); err != nil {
return "", err
}
return c.ToBocBase64()
}

func (h *Handler) GetWallets(ctx context.Context, params oas.GetWalletsParams) (*oas.WalletList, error) {
next, err := ton.ParseAccountID(params.NextFrom)
if err != nil {
Expand Down Expand Up @@ -217,5 +188,71 @@ func (h *Handler) GetWallets(ctx context.Context, params oas.GetWalletsParams) (
}
return &oas.WalletList{Wallets: items, NextFrom: nextFrom}, nil
}
}

func (h *Handler) getStateInit(ctx context.Context, owner ton.AccountID) (string, error) {
// TODO: add cache
var stateInit boc.Cell
err := retry.Do(func() error {
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
defer cancel()

_, value, err := GetWalletStateInitAndSalt(ctx, h.cli, h.jettonMaster, owner.ToMsgAddress())
if err != nil {
return err
}
result, ok := value.(GetWalletStateInitAndSaltResult)
if !ok {
return fmt.Errorf("failed to get state init")
}
stateInit = boc.Cell(result.StateInit)
return nil
}, retry.Attempts(3), retry.Delay(1*time.Second))
if err != nil {
return "", err
}
return stateInit.ToBocBase64()
}

type GetWalletStateInitAndSaltResult struct {
StateInit tlb.Any
Salt int64
}

func GetWalletStateInitAndSalt(ctx context.Context, executor abi.Executor, reqAccountID ton.AccountID, ownerAddress tlb.MsgAddress) (string, any, error) {
stack := tlb.VmStack{}
var (
val tlb.VmStackValue
err error
)
val, err = tlb.TlbStructToVmCellSlice(ownerAddress)
if err != nil {
return "", nil, err
}
stack.Put(val)

// MethodID = 69258 for "get_wallet_state_init_and_salt" method
errCode, stack, err := executor.RunSmcMethodByID(ctx, reqAccountID, 69258, 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){DecodeGetWalletStateInitAndSaltResult} {
s, r, err := f(stack)
if err == nil {
return s, r, nil
}
}
return "", nil, fmt.Errorf("can not decode outputs")
}

func DecodeGetWalletStateInitAndSaltResult(stack tlb.VmStack) (resultType string, resultAny any, err error) {
if len(stack) != 2 || (stack[0].SumType != "VmStkCell") || (stack[1].SumType != "VmStkTinyInt" && stack[1].SumType != "VmStkInt") {
return "", nil, fmt.Errorf("invalid stack format")
}
var result GetWalletStateInitAndSaltResult
err = stack.Unmarshal(&result)
return "GetWalletStateInitAndSaltResult", result, err
}
47 changes: 47 additions & 0 deletions pkg/api/handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package api

import (
"context"
"fmt"
"testing"

"github.com/stretchr/testify/require"
"github.com/tonkeeper/tongo/boc"
"github.com/tonkeeper/tongo/liteapi"
"github.com/tonkeeper/tongo/tlb"
"github.com/tonkeeper/tongo/ton"
"go.uber.org/zap"
)

func TestHandler_getStateInit(t *testing.T) {
tests := []struct {
name string
owner ton.AccountID
wantErr bool
}{
{
owner: ton.MustParseAccountID("0:6ccd325a858c379693fae2bcaab1c2906831a4e10a6c3bb44ee8b615bca1d220"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
logger, _ := zap.NewDevelopment()
cli, err := liteapi.NewClient(liteapi.Mainnet())
require.Nil(t, err)
h := &Handler{
cli: cli,
logger: logger,
jettonMaster: ton.MustParseAccountID("EQD6Z9DHc5Mx-8PI8I4BjGX0d2NhapaRAK12CgstweNoMint"),
}
stateInit, err := h.getStateInit(context.Background(), tt.owner)
require.Nil(t, err)
cells, err := boc.DeserializeBocBase64(stateInit)
require.Nil(t, err)
require.Equal(t, 1, len(cells))

var init tlb.StateInit
require.Nil(t, tlb.Unmarshal(cells[0], &init))
fmt.Printf("stateInit: %v\n", stateInit)
})
}
}

0 comments on commit fcf2d4d

Please sign in to comment.