Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 16 additions & 11 deletions contract/jetton/jetton.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ type Jetton struct {
}

type TransferMessage struct {
Jetton *Jetton
Sender ton.AccountID
Jetton *Jetton
Sender ton.AccountID
// SenderJettonWallet if nil, inferred from Sender + Jetton. Those are not required at all otherwise (for ToInternal)
SenderJettonWallet *ton.AccountID
JettonAmount *big.Int
Destination ton.AccountID
ResponseDestination *ton.AccountID
Expand Down Expand Up @@ -63,9 +65,17 @@ func (tm TransferMessage) ToInternal() (tlb.Message, uint8, error) {
if err := tlb.Marshal(c, msgBody); err != nil {
return tlb.Message{}, 0, err
}
jettonWallet, err := tm.Jetton.GetJettonWallet(context.TODO(), tm.Sender)
if err != nil {
return tlb.Message{}, 0, err
var jettonWallet ton.AccountID
if tm.SenderJettonWallet != nil {
jettonWallet = *tm.SenderJettonWallet
} else if tm.Jetton != nil {
var err error
jettonWallet, err = tm.Jetton.GetJettonWallet(context.TODO(), tm.Sender)
if err != nil {
return tlb.Message{}, 0, err
}
} else {
return tlb.Message{}, 0, errors.New("either SenderJettonWallet or Jetton must be set")
}
m := wallet.Message{
Amount: tm.AttachedGram,
Expand All @@ -75,12 +85,7 @@ func (tm TransferMessage) ToInternal() (tlb.Message, uint8, error) {
Body: c,
}
if tm.StateInit != nil {
if tm.StateInit.Code.Exists {
m.Code = &tm.StateInit.Code.Value.Value
}
if tm.StateInit.Data.Exists {
m.Data = &tm.StateInit.Data.Value.Value
}
m.Init = tm.StateInit
}
return m.ToInternal()
}
Expand Down
4 changes: 4 additions & 0 deletions tlb/primitives.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ type Either[M, N any] struct {
Right N
}

// EitherRef represent how Value is stored:
//
// "Left" is when Value is directly stored (in the same cell)
// "Right" is when Value stored in a next ref
type EitherRef[T any] struct {
IsRight bool
Value T
Expand Down
86 changes: 55 additions & 31 deletions wallet/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"crypto/ed25519"
"errors"
"fmt"
"slices"

"github.com/tonkeeper/tongo/boc"
"github.com/tonkeeper/tongo/tlb"
Expand Down Expand Up @@ -96,12 +97,7 @@ func (m *MessageV4) UnmarshalTLB(c *boc.Cell, decoder *tlb.Decoder) error {
Bounce: true,
Mode: 3,
}
if msg.Payload.DeployAndInstallPlugin.StateInit.Code.Exists {
out.Code = &msg.Payload.DeployAndInstallPlugin.StateInit.Code.Value.Value
}
if msg.Payload.DeployAndInstallPlugin.StateInit.Data.Exists {
out.Data = &msg.Payload.DeployAndInstallPlugin.StateInit.Data.Value.Value
}
out.Init = &msg.Payload.DeployAndInstallPlugin.StateInit
case "InstallPlugin":
m.Op = 2
bodyRef = false
Expand Down Expand Up @@ -162,7 +158,21 @@ type W5SendMessageAction struct {
Msg *boc.Cell `tlb:"^"`
}

type W5Actions []W5SendMessageAction
// W5ActionList messages for wallet to forward.
// slice order matches the order of wallet taking the corresponding actions
// if you used to refer this type as W5Actions, ensure that's this order is what you expect
type W5ActionList []W5SendMessageAction

func newW5Actions(internalMessages []RawMessage) W5ActionList {
actions := make([]W5SendMessageAction, len(internalMessages))
for i, msg := range internalMessages {
actions[i] = W5SendMessageAction{
Msg: msg.Message,
Mode: msg.Mode,
}
}
return actions
}

// MessageV5Beta is a message format used by wallet v5 beta.
type MessageV5Beta struct {
Expand All @@ -174,7 +184,7 @@ type MessageV5Beta struct {
Seqno uint32
Op bool
Signature tlb.Bits512
Actions W5Actions `tlb:"^"`
Actions W5ActionList `tlb:"^"`
} `tlbSumType:"#73696e74"`
// SignedExternal is an external message authenticated by a signature.
SignedExternal struct {
Expand All @@ -183,7 +193,7 @@ type MessageV5Beta struct {
Seqno uint32
Op bool
Signature tlb.Bits512
Actions W5Actions `tlb:"^"`
Actions W5ActionList `tlb:"^"`
} `tlbSumType:"#7369676e"`
}

Expand All @@ -195,7 +205,7 @@ type MessageV5 struct {
WalletId uint32
ValidUntil uint32
Seqno uint32
Actions *W5Actions `tlb:"maybe^"`
Actions *W5ActionList `tlb:"maybe^"`
ExtendedActions *W5ExtendedActions `tlb:"maybe"`
Signature tlb.Bits512
} `tlbSumType:"#73696e74"`
Expand All @@ -204,13 +214,13 @@ type MessageV5 struct {
WalletId uint32
ValidUntil uint32
Seqno uint32
Actions *W5Actions `tlb:"maybe^"`
Actions *W5ActionList `tlb:"maybe^"`
ExtendedActions *W5ExtendedActions `tlb:"maybe"`
Signature tlb.Bits512
} `tlbSumType:"#7369676e"`
ExtensionAction *struct {
QueryID uint64
Actions *W5Actions `tlb:"maybe^"`
Actions *W5ActionList `tlb:"maybe^"`
ExtendedActions *W5ExtendedActions `tlb:"maybe"`
} `tlbSumType:"#6578746e"`
}
Expand All @@ -235,6 +245,19 @@ type RawMessage struct {
Mode byte
}

// ToRawMessage prepares internal message for a wallet to forward
func ToRawMessage(message Sendable) (RawMessage, error) {
intMsg, mode, err := message.ToInternal()
if err != nil {
return RawMessage{}, err
}
cell := boc.NewCell()
if err := tlb.Marshal(cell, intMsg); err != nil {
return RawMessage{}, err
}
return RawMessage{Message: cell, Mode: mode}, nil
}

type PayloadV1toV4 []RawMessage
type PayloadHighload []RawMessage

Expand Down Expand Up @@ -494,11 +517,12 @@ func (p *PayloadHighload) UnmarshalTLB(c *boc.Cell, decoder *tlb.Decoder) error
return nil
}

func (l *W5Actions) UnmarshalTLB(c *boc.Cell, decoder *tlb.Decoder) error {
func (l *W5ActionList) UnmarshalTLB(c *boc.Cell, decoder *tlb.Decoder) error {
var actions []W5SendMessageAction
for {
switch c.BitsAvailableForRead() {
case 0:
slices.Reverse(actions)
*l = actions
return nil
case 40:
Expand All @@ -518,27 +542,27 @@ func (l *W5Actions) UnmarshalTLB(c *boc.Cell, decoder *tlb.Decoder) error {
}
}

func (l W5Actions) MarshalTLB(c *boc.Cell, encoder *tlb.Encoder) error {
func (l W5ActionList) MarshalTLB(c *boc.Cell, _ *tlb.Encoder) error {
if len(l) == 0 {
return nil
}
if err := c.WriteUint(0x0ec3c86d, 32); err != nil {
return err
}
action := l[0]
if err := c.WriteUint(uint64(action.Mode), 8); err != nil {
return err
}
cell := boc.NewCell()
next := l[1:]
if err := encoder.Marshal(cell, next); err != nil {
return err
}
if err := c.AddRef(cell); err != nil {
return err
}
if err := c.AddRef(action.Msg); err != nil {
return err

for i := len(l) - 1; i >= 0; i-- {
action := l[i]
if err := c.WriteUint(0x0ec3c86d, 32); err != nil {
return err
}
if err := c.WriteUint(uint64(action.Mode), 8); err != nil {
return err
}
prev := boc.NewCell()
if err := c.AddRef(prev); err != nil {
return err
}
if err := c.AddRef(action.Msg); err != nil {
return err
}
c = prev
}
return nil
}
Expand Down
8 changes: 4 additions & 4 deletions wallet/messages_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,15 @@ func TestExtractRawMessages(t *testing.T) {
ver: V5Beta,
want: []RawMessage{
{
Message: mustFromBase64("te6ccgEBAQEAVwAAqWgB6G+oc+JDZG77OSjCMnH/ugluoRhGeUI6497n7aCbbhcAGzNMlqFjDeWk/rivKqxwpBoMaThCmw7tE7othW8odIgMtxsAAAAAAAAAAAAAAAAAAEA="),
Message: mustFromBase64("te6ccgEBAQEAVwAAqWgB6G+oc+JDZG77OSjCMnH/ugluoRhGeUI6497n7aCbbhcAGzNMlqFjDeWk/rivKqxwpBoMaThCmw7tE7othW8odIgMPQkAAAAAAAAAAAAAAAAAAEA="),
Mode: 3,
},
{
Message: mustFromBase64("te6ccgEBAQEAVwAAqWgB6G+oc+JDZG77OSjCMnH/ugluoRhGeUI6497n7aCbbhcAGzNMlqFjDeWk/rivKqxwpBoMaThCmw7tE7othW8odIgMehIAAAAAAAAAAAAAAAAAAEA="),
Mode: 3,
},
{
Message: mustFromBase64("te6ccgEBAQEAVwAAqWgB6G+oc+JDZG77OSjCMnH/ugluoRhGeUI6497n7aCbbhcAGzNMlqFjDeWk/rivKqxwpBoMaThCmw7tE7othW8odIgMPQkAAAAAAAAAAAAAAAAAAEA="),
Message: mustFromBase64("te6ccgEBAQEAVwAAqWgB6G+oc+JDZG77OSjCMnH/ugluoRhGeUI6497n7aCbbhcAGzNMlqFjDeWk/rivKqxwpBoMaThCmw7tE7othW8odIgMtxsAAAAAAAAAAAAAAAAAAEA="),
Mode: 3,
},
},
Expand All @@ -125,11 +125,11 @@ func TestExtractRawMessages(t *testing.T) {
ver: V5R1,
want: []RawMessage{
{
Message: mustFromBase64("te6ccgEBAQEANQAAZkIAa5yKA/gbStnXmg2U1Zib1GE01YSPACHaGA0K0BpBRw4aYloAAAAAAAAAAAAAAAAAAA=="),
Message: mustFromBase64("te6ccgEBAQEANQAAZkIAa5yKA/gbStnXmg2U1Zib1GE01YSPACHaGA0K0BpBRw4cxLQAAAAAAAAAAAAAAAAAAA=="),
Mode: 3,
},
{
Message: mustFromBase64("te6ccgEBAQEANQAAZkIAa5yKA/gbStnXmg2U1Zib1GE01YSPACHaGA0K0BpBRw4cxLQAAAAAAAAAAAAAAAAAAA=="),
Message: mustFromBase64("te6ccgEBAQEANQAAZkIAa5yKA/gbStnXmg2U1Zib1GE01YSPACHaGA0K0BpBRw4aYloAAAAAAAAAAAAAAAAAAA=="),
Mode: 3,
},
},
Expand Down
50 changes: 34 additions & 16 deletions wallet/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ package wallet
import (
"context"
"fmt"
"github.com/tonkeeper/tongo/utils"
"math/big"
"time"

"github.com/tonkeeper/tongo/boc"
"github.com/tonkeeper/tongo/tlb"
"github.com/tonkeeper/tongo/ton"
"github.com/tonkeeper/tongo/utils"
)

type Version int
Expand Down Expand Up @@ -187,6 +187,25 @@ func VersionFromString(s string) (Version, error) {
return v, nil
}

func GetVersionByCode(code boc.Cell) (Version, error) {
hash, err := code.Hash256()
if err != nil {
return 0, fmt.Errorf("failed to calculate code hash: %w", err)
}
version, ok := GetVerByCodeHash(hash)
if !ok {
return 0, fmt.Errorf("not a wallet")
}
return version, nil
}

// SupportsRelay reports whether this wallet version can execute a signed message body
// delivered inside an internal message (the internal_signed opcode)
// see https://docs.ton.org/contracts/standard/wallets/gasless
func (v Version) SupportsRelay() bool {
return v == V5R1 || v == V5Beta
}

type Sendable interface {
ToInternal() (tlb.Message, uint8, error)
}
Expand Down Expand Up @@ -246,11 +265,13 @@ func (m SimpleTransfer) ToInternal() (message tlb.Message, mode uint8, err error
type Message struct {
Amount tlb.Grams
Address ton.AccountID
Body *boc.Cell
Code *boc.Cell
Data *boc.Cell
Bounce bool
Mode uint8
// Src is the sender address. Usually, nil is used (if message will be external)
// for relayed messages (gasless), non-nill src is required
Src *ton.AccountID
Body *boc.Cell
Init *tlb.StateInit
Bounce bool
Mode uint8
}

func (m Message) ToInternal() (message tlb.Message, mode uint8, err error) {
Expand All @@ -272,7 +293,7 @@ func (m Message) ToInternal() (message tlb.Message, mode uint8, err error) {
}{
IhrDisabled: true,
Bounce: m.Bounce,
Src: (*ton.AccountID)(nil).ToMsgAddress(),
Src: m.Src.ToMsgAddress(),
Dest: m.Address.ToMsgAddress(),
IhrFee: tlb.VarUInteger16(*big.NewInt(0)),
}
Expand All @@ -286,13 +307,11 @@ func (m Message) ToInternal() (message tlb.Message, mode uint8, err error) {
intMsg.Body.IsRight = true //todo: check length and
intMsg.Body.Value = tlb.Any(*m.Body)
}
if m.Code != nil && m.Data != nil {
intMsg.Init.Exists = true
intMsg.Init.Value.IsRight = true
intMsg.Init.Value.Value.Code.Exists = true
intMsg.Init.Value.Value.Data.Exists = true
intMsg.Init.Value.Value.Code.Value.Value = *m.Code
intMsg.Init.Value.Value.Data.Value.Value = *m.Data
if m.Init != nil {
intMsg.Init = tlb.Just(tlb.EitherRef[tlb.StateInit]{
IsRight: true,
Value: *m.Init,
})
}

return intMsg, m.Mode, nil
Expand Down Expand Up @@ -367,8 +386,7 @@ func (cd ContractDeploy) ToInternal() (tlb.Message, uint8, error) {
Amount: cd.Amount,
Address: ton.AccountID{cd.Workchain, hash},
Body: body,
Code: code,
Data: data,
Init: &init,
Bounce: true,
Mode: 3,
}
Expand Down
Loading
Loading