diff --git a/examples/concurrent-transfer-berkeley.eg.ts b/examples/concurrent-transfer-berkeley.eg.ts new file mode 100644 index 0000000..84ff816 --- /dev/null +++ b/examples/concurrent-transfer-berkeley.eg.ts @@ -0,0 +1,155 @@ +import { equal } from "node:assert" +import { AccountUpdate, Mina, PrivateKey, PublicKey, UInt64, fetchAccount } from "o1js" +import { FungibleToken } from "../index.js" + +const url = "https://proxy.berkeley.minaexplorer.com/graphql" + +// we use it here because some transactions from sender may be already in a mempool, +// resulting in rejection of a transaction with same nonce +// this is demonstrated in mint phase (we don't wait after first mint to include a second one) +async function getInferredNonce(publicKey: string) { + const query = ` +query { + account(publicKey: "${publicKey}") { + inferredNonce + } +}` + + const response = await fetch( + url, + { + method: "POST", + body: JSON.stringify({ operationName: null, query, variables: {} }), + headers: { "Content-Type": "application/json" }, + }, + ) + const json = await response.json() + return Number(json.data.account.inferredNonce) +} + +const sleep = (ms: number) => new Promise(r => setTimeout(r, ms)); + +const berkeley = Mina.Network(url) +Mina.setActiveInstance(berkeley) + +const fee = 1e8 + +const deployerKey = PrivateKey.fromBase58("EKE5nJtRFYVWqrCfdpqJqKKdt2Sskf5Co2q8CWJKEGSg71ZXzES7") +const deployer = { + publicKey: deployerKey.toPublicKey(), + privateKey: deployerKey, +} + +const [alexa, billy, contract] = [ + PrivateKey.randomKeypair(), + PrivateKey.randomKeypair(), + PrivateKey.randomKeypair(), +] + +contract.publicKey = PublicKey.fromBase58('B62qjUrGPK1vePNUNkKfnNfxoAodfdzesBBwHfRfeQuBdxWp9kVMy7n') + +console.log(` +alexa ${alexa.publicKey.toBase58()} +billy ${billy.publicKey.toBase58()} +contract ${contract.publicKey.toBase58()} +`) + +await FungibleToken.compile() +const token = new FungibleToken(contract.publicKey) +await fetchAccount({publicKey: contract.publicKey}) +// let nonce = await getInferredNonce(deployer.publicKey.toBase58()) +// console.log("Deploying token contract.") +// console.log("Nonce:", nonce) +// const deployTx = await Mina.transaction({ +// sender: deployer.publicKey, +// fee, +// nonce, +// }, () => { +// AccountUpdate.fundNewAccount(deployer.publicKey, 1) +// token.deploy({ +// owner: deployer.publicKey, +// supply: UInt64.from(10_000_000_000_000), +// symbol: "abc", +// src: "https://github.com/MinaFoundation/mina-fungible-token/blob/main/examples/e2e.eg.ts", +// }) +// }) +// await deployTx.prove() +// deployTx.sign([deployer.privateKey, contract.privateKey]) +// const deployTxResult = await deployTx.send().then((v) => v.wait()) +// console.log("Deploy tx:", deployTxResult.hash) +// equal(deployTxResult.status, "included") + +console.log("Minting new tokens to deployer.") +let nonce = await getInferredNonce(deployer.publicKey.toBase58()) +console.log("Nonce:", nonce) +const mintTx1 = await Mina.transaction({ + // using deployer to pay fees because this is the only one who has tokens + sender: deployer.publicKey, + fee, + nonce, +}, () => { + AccountUpdate.fundNewAccount(deployer.publicKey, 1) + token.mint(deployer.publicKey, new UInt64(2e9)) +}) + +await mintTx1.prove() +mintTx1.sign([deployer.privateKey]) +const mintTxResult1 = await mintTx1.send() +console.log("Mint tx 1:", mintTxResult1.hash) +await mintTxResult1.wait() + + +console.log("[1] Transfer tokens to Billy.") +nonce = await getInferredNonce(deployer.publicKey.toBase58()) +console.log("Nonce:", nonce) + +const transferTx1 = await Mina.transaction({ + // using deployer to pay fees because this is the only one who has tokens + sender: deployer.publicKey, + fee, + nonce, +}, () => { + AccountUpdate.fundNewAccount(deployer.publicKey, 1) + token.transfer(deployer.publicKey, billy.publicKey, UInt64.from(1e9)) +}) + +await transferTx1.prove() +transferTx1.sign([deployer.privateKey]) +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()) + console.log(au.toPretty().preconditions) + console.log(au.toPretty().update) +} + +console.log("[2] Transfer tokens to Billy.") +nonce += 1 +console.log("Nonce:", nonce) + +const transferTx2 = await Mina.transaction({ + // using deployer to pay fees because this is the only one who has tokens + sender: deployer.publicKey, + fee, + nonce, +}, () => { + token.transfer(deployer.publicKey, billy.publicKey, UInt64.from(1e9)) +}) + +await transferTx2.prove() +transferTx2.sign([deployer.privateKey]) +const transferTxResult2 = await transferTx2.send() +console.log("Transfer tx 2:", transferTxResult2.hash) + +console.log('Transfer tx 2 Account Updates') +for (let au of transferTx2.transaction.accountUpdates) { + console.log(au.publicKey.toBase58()) + console.log(au.toPretty().preconditions) + console.log(au.toPretty().update) +} + +const transferTx2Included = await transferTxResult2.wait() +equal(transferTx2Included.status, "included") + diff --git a/examples/e2e.eg.ts b/examples/e2e.eg.ts index a4a51ea..5ce8453 100644 --- a/examples/e2e.eg.ts +++ b/examples/e2e.eg.ts @@ -58,7 +58,7 @@ console.log("Alexa balance after mint:", alexaBalanceAfterMint) equal(alexaBalanceAfterMint, BigInt(2e9)) const billyBalanceBeforeMint = token.getBalanceOf(billy.publicKey) -console.log("Billy balance before mint:", billyBalanceBeforeMint.toBigInt()) +console.log("Billy balance before transfer:", billyBalanceBeforeMint.toBigInt()) equal(alexaBalanceBeforeMint, 0n) console.log("Transferring tokens from Alexa to Billy") @@ -66,11 +66,11 @@ const transferTx = await Mina.transaction({ sender: alexa.publicKey, fee, }, () => { - AccountUpdate.fundNewAccount(billy.publicKey, 1) + AccountUpdate.fundNewAccount(alexa.publicKey, 1) token.transfer(alexa.publicKey, billy.publicKey, new UInt64(1e9)) }) await transferTx.prove() -transferTx.sign([alexa.privateKey, billy.privateKey]) +transferTx.sign([alexa.privateKey]) const transferTxResult = await transferTx.send().then((v) => v.wait()) console.log("Transfer tx result:", transferTxResult.toPretty()) equal(transferTxResult.status, "included")