Cannot transfer custom tokens more than once
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