o1-labs / o1js

TypeScript framework for zk-SNARKs and zkApps

Home Page:https://docs.minaprotocol.com/en/zkapps/how-to-write-a-zkapp

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Cannot transfer custom tokens more than once

qwadratic opened this issue · comments

Cannot transfer custom tokens more than once

For some reason, there is an implicit precondition on the sender account update (with tokenId of custom token) that requires the nonce to be always zero.

Here is the code to reproduce. To save your time, I precooked a test address, deployed and minted some tokens.
This code for transfer worked out only for the first time. It doesn't work anymore because of that nonce precondition

import { AccountUpdate, Mina, PrivateKey, Provable, PublicKey, TokenId, UInt64, fetchAccount } from "o1js"
import { FungibleToken } from "mina-fungible-token"

const url = "https://proxy.berkeley.minaexplorer.com/graphql"

const berkeley = Mina.Network(url)
Mina.setActiveInstance(berkeley)

const fee = 1e8

const deployerKey = PrivateKey.fromBase58("EKE5nJtRFYVWqrCfdpqJqKKdt2Sskf5Co2q8CWJKEGSg71ZXzES7")
const deployer = deployerKey.toPublicKey()

const billy = PrivateKey.randomKeypair()

const contract = PublicKey.fromBase58('B62qjUrGPK1vePNUNkKfnNfxoAodfdzesBBwHfRfeQuBdxWp9kVMy7n')

console.log(`
  deployer ${deployer.toBase58()}
  billy ${billy.publicKey.toBase58()}
  contract ${contract.toBase58()}
`)

await FungibleToken.compile()
const token = new FungibleToken(contract)

console.log("[1] Transfer tokens to Billy.")

const transferTx1 = await Mina.transaction({
  sender: deployer,
  fee,
}, () => {
  AccountUpdate.fundNewAccount(deployer, 1)
  token.transfer(deployer, billy.publicKey, UInt64.from(1e9))
})

await transferTx1.prove()

transferTx1.sign([deployerKey])
const transferTxResult1 = await transferTx1.send()
console.log("Transfer tx 1:", transferTxResult1.hash)

console.log('Transfer tx 1 Account Updates')
for (let au of transferTx1.transaction.accountUpdates) {
  console.log(au.publicKey.toBase58(), TokenId.toBase58(au.tokenId))
  console.log(au.toPretty().preconditions)
  console.log(au.toPretty().update)
}

await transferTxResult1.wait()
Logs
  deployer B62qmVz7pPiLXPvz2nPkuK3K5akjrePAVtdBVMfeyixrccgqKTQte8K
  billy B62qk5pcWnducEnxrCRJ2y5XexKfrRQGN7GSjjUy7VGmQ6V9FikBRxr
  contract B62qjUrGPK1vePNUNkKfnNfxoAodfdzesBBwHfRfeQuBdxWp9kVMy7n

[1] Transfer tokens to Billy.
Transfer tx 1: 5Jtx7dkRLWq1rBrCi5Ut41kKs1eahDJxjL9XiD9eMQ8EpFZGYTSk
Transfer tx 1 Account Updates
B62qmVz7pPiLXPvz2nPkuK3K5akjrePAVtdBVMfeyixrccgqKTQte8K wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf
undefined
undefined
B62qjUrGPK1vePNUNkKfnNfxoAodfdzesBBwHfRfeQuBdxWp9kVMy7n wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf
undefined
undefined
B62qjUrGPK1vePNUNkKfnNfxoAodfdzesBBwHfRfeQuBdxWp9kVMy7n wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf
undefined
undefined
B62qmVz7pPiLXPvz2nPkuK3K5akjrePAVtdBVMfeyixrccgqKTQte8K woxTkXc9zNDiy8kWoYGXzifVZmUrZyQj7rz6qRZzqzSAwrRXTS
{ account: '{"nonce":{"lower":"0","upper":"0"}}' }
undefined
B62qqEgTgtFo4fM8LNrQzbiwhFZopZgbjf7ioykVBb2j49w3dPaWHc3 woxTkXc9zNDiy8kWoYGXzifVZmUrZyQj7rz6qRZzqzSAwrRXTS
undefined
undefined
/Users/i/mina/mip-token-standard/node_modules/o1js/dist/node/bindings/compiled/_node_bindings/o1js_node.bc.cjs:6799
         throw err;
         ^

Error: Transaction failed with errors:
[[["Cancelled"]],[["Cancelled"]],[["Cancelled"]],[["Account_nonce_precondition_unsatisfied"]],[["Cancelled"]]]
    at Object.wait (file:///Users/i/mina/mip-token-standard/node_modules/o1js/dist/node/lib/mina.js:190:27)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async file:///Users/i/mina/mip-token-standard/examples/transfer-berkeley.eg.ts:36:1

Node.js v21.1.0

Not sure why, but the correct way is to use .internal.send()

.transfer doesn't work, because it uses requireSignature which sets nonce precondition to 0 all the time.

.internal.send() uses setLazySignature instead

not sure whats the difference.

This is a bug, requireSignature() should fetch and use the correct nonce