{"version":3,"file":"builder.mjs","names":["#host","#path","#client","#contract","#objectsToTransfer"],"sources":["../../src/links/builder.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { decodeSuiPrivateKey } from '@mysten/sui/cryptography';\nimport type { Keypair, Signer } from '@mysten/sui/cryptography';\nimport { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';\nimport type { TransactionObjectArgument } from '@mysten/sui/transactions';\nimport { Transaction } from '@mysten/sui/transactions';\nimport { normalizeStructTag, normalizeSuiAddress, SUI_TYPE_ARG, toBase64 } from '@mysten/sui/utils';\n\nimport type { ZkBagContractOptions } from './zk-bag.js';\nimport { getContractIds, ZkBag } from './zk-bag.js';\nimport type { ClientWithCoreApi } from '@mysten/sui/client';\n\nexport interface ZkSendLinkBuilderOptions {\n\thost?: string;\n\tpath?: string;\n\tkeypair?: Keypair;\n\tnetwork?: string;\n\tclient: ClientWithCoreApi;\n\tsender: string;\n\tcontract?: ZkBagContractOptions;\n}\n\nconst DEFAULT_ZK_SEND_LINK_OPTIONS = {\n\thost: 'https://my.slush.app',\n\tpath: '/claim',\n\tnetwork: 'mainnet' as const,\n};\n\nconst SUI_COIN_TYPE = normalizeStructTag(SUI_TYPE_ARG);\n\nexport interface CreateZkSendLinkOptions {\n\ttransaction?: Transaction;\n}\n\nexport class ZkSendLinkBuilder {\n\tobjectIds = new Set<string>();\n\tobjectRefs: {\n\t\tref: TransactionObjectArgument;\n\t\ttype: string;\n\t}[] = [];\n\tbalances = new Map<string, bigint>();\n\tsender: string;\n\tnetwork: string;\n\t#host: string;\n\t#path: string;\n\tkeypair: Keypair;\n\t#client: ClientWithCoreApi;\n\t#contract: ZkBag<ZkBagContractOptions>;\n\n\tconstructor({\n\t\thost = DEFAULT_ZK_SEND_LINK_OPTIONS.host,\n\t\tpath = DEFAULT_ZK_SEND_LINK_OPTIONS.path,\n\t\tkeypair = new Ed25519Keypair(),\n\t\tnetwork,\n\t\tclient,\n\t\tsender,\n\t\tcontract,\n\t}: ZkSendLinkBuilderOptions) {\n\t\tconst resolvedNetwork = network ?? client.network;\n\t\tconst resolvedContract = contract ?? getContractIds(resolvedNetwork as 'mainnet' | 'testnet');\n\t\tthis.#host = host;\n\t\tthis.#path = path;\n\t\tthis.keypair = keypair;\n\t\tthis.#client = client;\n\t\tthis.sender = normalizeSuiAddress(sender);\n\t\tthis.network = resolvedNetwork;\n\n\t\tthis.#contract = new ZkBag(resolvedContract.packageId, resolvedContract);\n\t}\n\n\taddClaimableMist(amount: bigint) {\n\t\tthis.addClaimableBalance(SUI_COIN_TYPE, amount);\n\t}\n\n\taddClaimableBalance(coinType: string, amount: bigint) {\n\t\tconst normalizedType = normalizeStructTag(coinType);\n\t\tthis.balances.set(normalizedType, (this.balances.get(normalizedType) ?? 0n) + amount);\n\t}\n\n\taddClaimableObject(id: string) {\n\t\tthis.objectIds.add(id);\n\t}\n\n\taddClaimableObjectRef(ref: TransactionObjectArgument, type: string) {\n\t\tthis.objectRefs.push({ ref, type });\n\t}\n\n\tgetLink(): string {\n\t\tconst link = new URL(this.#host);\n\t\tlink.pathname = this.#path;\n\t\t// NOTE: The $ prefix here is intentional, and not a typo, it is used to designate a contract-based link\n\t\tlink.hash = `$${toBase64(decodeSuiPrivateKey(this.keypair.getSecretKey()).secretKey)}`;\n\n\t\tif (this.network !== 'mainnet') {\n\t\t\tlink.searchParams.set('network', this.network);\n\t\t}\n\n\t\treturn link.toString();\n\t}\n\n\tasync create({\n\t\tsigner,\n\t\t...options\n\t}: CreateZkSendLinkOptions & {\n\t\tsigner: Signer;\n\t\twaitForTransaction?: boolean;\n\t}) {\n\t\tconst tx = await this.createSendTransaction(options);\n\n\t\tconst result = await signer.signAndExecuteTransaction({\n\t\t\ttransaction: tx,\n\t\t\tclient: this.#client,\n\t\t});\n\n\t\tif (result.FailedTransaction) {\n\t\t\tthrow new Error(\n\t\t\t\t`Transaction failed: ${result.FailedTransaction.status.error?.message ?? 'Unknown error'}`,\n\t\t\t);\n\t\t}\n\n\t\tif (options.waitForTransaction) {\n\t\t\tawait this.#client.core.waitForTransaction({ result });\n\t\t}\n\n\t\treturn result;\n\t}\n\tasync createSendTransaction({ transaction = new Transaction() }: CreateZkSendLinkOptions = {}) {\n\t\ttransaction.setSenderIfNotSet(this.sender);\n\n\t\treturn ZkSendLinkBuilder.createLinks({\n\t\t\ttransaction,\n\t\t\tnetwork: this.network,\n\t\t\tclient: this.#client,\n\t\t\tcontract: this.#contract.ids,\n\t\t\tlinks: [this],\n\t\t});\n\t}\n\n\tasync createSendToAddressTransaction({\n\t\ttransaction = new Transaction(),\n\t\taddress,\n\t}: CreateZkSendLinkOptions & {\n\t\taddress: string;\n\t}) {\n\t\tconst objectsToTransfer = (await this.#objectsToTransfer(transaction)).map((obj) => obj.ref);\n\n\t\ttransaction.setSenderIfNotSet(this.sender);\n\t\ttransaction.transferObjects(objectsToTransfer, address);\n\n\t\treturn transaction;\n\t}\n\n\tasync #objectsToTransfer(tx: Transaction) {\n\t\tconst objectIDs = [...this.objectIds];\n\t\tconst refsWithType = this.objectRefs.concat(\n\t\t\t(objectIDs.length > 0\n\t\t\t\t? await this.#client.core.getObjects({\n\t\t\t\t\t\tobjectIds: objectIDs,\n\t\t\t\t\t})\n\t\t\t\t: { objects: [] }\n\t\t\t).objects.map((res, i) => {\n\t\t\t\tif (res instanceof Error) {\n\t\t\t\t\tthrow new Error(`Failed to load object ${objectIDs[i]} (${res.message})`);\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tref: tx.objectRef({\n\t\t\t\t\t\tversion: res.version,\n\t\t\t\t\t\tdigest: res.digest,\n\t\t\t\t\t\tobjectId: res.objectId,\n\t\t\t\t\t}),\n\t\t\t\t\ttype: res.type,\n\t\t\t\t};\n\t\t\t}),\n\t\t);\n\n\t\tfor (const [coinType, amount] of this.balances) {\n\t\t\trefsWithType.push({\n\t\t\t\tref: tx.coin({ type: coinType, balance: amount }),\n\t\t\t\ttype: `0x2::coin::Coin<${coinType}>`,\n\t\t\t});\n\t\t}\n\n\t\treturn refsWithType;\n\t}\n\n\tstatic async createLinks({\n\t\tlinks,\n\t\tnetwork = 'mainnet',\n\t\tclient,\n\t\ttransaction = new Transaction(),\n\t\tcontract: contractIds,\n\t}: {\n\t\ttransaction?: Transaction;\n\t\tclient: ClientWithCoreApi;\n\t\tnetwork?: string;\n\t\tlinks: ZkSendLinkBuilder[];\n\t\tcontract?: ZkBagContractOptions;\n\t}) {\n\t\tconst resolvedContractIds = contractIds ?? getContractIds(network as 'mainnet' | 'testnet');\n\t\tconst contract = new ZkBag(resolvedContractIds.packageId, resolvedContractIds);\n\t\tconst store = transaction.object(contract.ids.bagStoreId);\n\n\t\tconst allIds = links.flatMap((link) => [...link.objectIds]);\n\t\tconst sender = links[0].sender;\n\t\ttransaction.setSenderIfNotSet(sender);\n\n\t\tconst objectRefs = new Map<\n\t\t\tstring,\n\t\t\t{\n\t\t\t\tref: TransactionObjectArgument;\n\t\t\t\ttype: string;\n\t\t\t}\n\t\t>();\n\n\t\tconst pageSize = 50;\n\t\tlet offset = 0;\n\t\twhile (offset < allIds.length) {\n\t\t\tconst chunk = allIds.slice(offset, offset + pageSize);\n\t\t\toffset += pageSize;\n\n\t\t\tconst objects = await client.core.getObjects({\n\t\t\t\tobjectIds: chunk,\n\t\t\t});\n\n\t\t\tfor (const [i, res] of objects.objects.entries()) {\n\t\t\t\tif (res instanceof Error) {\n\t\t\t\t\tthrow new Error(`Failed to load object ${chunk[i]} (${res.message})`);\n\t\t\t\t}\n\n\t\t\t\tobjectRefs.set(chunk[i], {\n\t\t\t\t\tref: transaction.objectRef({\n\t\t\t\t\t\tversion: res.version,\n\t\t\t\t\t\tdigest: res.digest,\n\t\t\t\t\t\tobjectId: res.objectId,\n\t\t\t\t\t}),\n\t\t\t\t\ttype: res.type!,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tfor (const link of links) {\n\t\t\tconst receiver = link.keypair.toSuiAddress();\n\t\t\ttransaction.add(contract.new({ arguments: [store, receiver] }));\n\n\t\t\tlink.objectRefs.forEach(({ ref, type }) => {\n\t\t\t\ttransaction.add(\n\t\t\t\t\tcontract.add({\n\t\t\t\t\t\targuments: [store, receiver, ref],\n\t\t\t\t\t\ttypeArguments: [type],\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t});\n\n\t\t\tlink.objectIds.forEach((id) => {\n\t\t\t\tconst object = objectRefs.get(id);\n\t\t\t\tif (!object) {\n\t\t\t\t\tthrow new Error(`Object ${id} not found`);\n\t\t\t\t}\n\t\t\t\ttransaction.add(\n\t\t\t\t\tcontract.add({\n\t\t\t\t\t\targuments: [store, receiver, object.ref],\n\t\t\t\t\t\ttypeArguments: [object.type],\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t});\n\n\t\t\tfor (const [coinType, balance] of link.balances) {\n\t\t\t\ttransaction.add(\n\t\t\t\t\tcontract.add({\n\t\t\t\t\t\targuments: [store, receiver, transaction.coin({ type: coinType, balance })],\n\t\t\t\t\t\ttypeArguments: [`0x2::coin::Coin<${coinType}>`],\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn transaction;\n\t}\n}\n"],"mappings":"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"}