tact-lang / tact

Tact compiler main repository
https://tact-lang.org
MIT License
371 stars 103 forks source link

contract in mainnet throws 9 cell underflow #661

Closed sergeipalii closed 1 month ago

sergeipalii commented 1 month ago

Working with cells is a bit confusing. simple contract in the mainnet ( EQDhBY35RWxzEsFnotxKo6v92T-b2crtrLlNsY_FBFGgWQt2 )

import "@stdlib/deploy";

contract SendToUsdt with Deployable {
  referrer: Address? = null;

  receive(msg: TokenNotification) {
    let payload = msg.forwardPayload;
    self.referrer = payload.loadAddress();
  }

  get fun referrer(): Address? {
    return self.referrer;
  }
}

message(0x7362d09c) TokenNotification {
  queryId: Int as uint64;
  amount: Int as coins;
  from: Address;
  forwardPayload: Slice as remaining;
}

code for sending tokens:

let forwardPayload = beginCell()
    .storeAddress(sender)
    .endCell();

  const messageBody = beginCell()
    .storeUint(0x0f8a7ea5, 32) // opcode for jetton transfer
    .storeUint(0n, 64) // query id
    .storeCoins(1000n) // token amount
    .storeAddress(dex) // destination
    .storeAddress(sender) // response destination
    .storeBit(0) // no custom payload
    .storeCoins(toNano('0.02')) // forward amount - if >0, will send notification message
    .storeBit(1) // we store forwardPayload as a reference
    .storeRef(forwardPayload)
    .endCell();

  const internalMessage = internal({
      to: usdtSenderWallet.toString(),
      value: toNano('0.04'),
      bounce: true,
      body: messageBody
  });

  await myWalletContract.sendTransfer({
      seqno,
      secretKey: keyPair.secretKey,
      messages: [internalMessage]
  });

When send message contract throws 9 cell underflow:

https://tonviewer.com/transaction/3d11768312c97bbe06ddffcfc974c53ee0d5c50cddefdb3d9273b0e051bbe1b6

Gusarich commented 1 month ago

The problem is same as in #659 - you don't take the "Either" bit into account in forward payload parsing.

Look at how you compose forward payload:

.storeBit(1) // we store forwardPayload as a reference
.storeRef(forwardPayload)

and how you read it:

self.referrer = payload.loadAddress();

You should instead read it like this:

let forwardPayload: Slice = payload.loadBool() ? payload.loadRef().beginParse() : payload;
self.referrer = forwardPayload.loadAddress();
sergeipalii commented 1 month ago

Thank you! It works.