@mysten/zksend
Advanced tools
Comparing version 0.0.0-experimental-20240313015203 to 0.0.0-experimental-20240319165746
# @mysten/zksend | ||
## 0.0.0-experimental-20240313015203 | ||
## 0.0.0-experimental-20240319165746 | ||
### Minor Changes | ||
- c05a4e8cb7: removed listClaimableAssets, and added new assets and claimed properties to link instances | ||
- c05a4e8cb7: Use contract by default for new links | ||
- c05a4e8cb7: Add helper for bulk link creation | ||
- c05a4e8cb7: Removed options for filtering claims | ||
- c05a4e8cb7: renamed loadOwnedData to loadAssets | ||
## 0.3.1 | ||
### Patch Changes | ||
@@ -6,0 +16,0 @@ |
@@ -20,4 +20,4 @@ import type { Output } from 'valibot'; | ||
address: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>], { | ||
@@ -31,4 +31,4 @@ type: "connect"; | ||
address: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>; | ||
@@ -58,4 +58,4 @@ export type ZkSendRequestData = Output<typeof ZkSendRequestData>; | ||
address: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>], { | ||
@@ -69,4 +69,4 @@ type: "connect"; | ||
address: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>; | ||
@@ -84,4 +84,4 @@ }, undefined, { | ||
address: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}; | ||
@@ -103,4 +103,4 @@ name?: string | undefined; | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
}>, import("valibot").ObjectSchema<{ | ||
@@ -112,4 +112,4 @@ type: import("valibot").LiteralSchema<"sign-personal-message", "sign-personal-message">; | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>], { | ||
@@ -120,8 +120,8 @@ address: string; | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
} | { | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>; | ||
@@ -147,4 +147,4 @@ export type ZkSendResponseData = Output<typeof ZkSendResponseData>; | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
}>, import("valibot").ObjectSchema<{ | ||
@@ -156,4 +156,4 @@ type: import("valibot").LiteralSchema<"sign-personal-message", "sign-personal-message">; | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>], { | ||
@@ -164,8 +164,8 @@ address: string; | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
} | { | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>; | ||
@@ -178,8 +178,8 @@ }, undefined, { | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
} | { | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}; | ||
@@ -195,8 +195,8 @@ type: "resolve"; | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
} | { | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}; | ||
@@ -227,4 +227,4 @@ type: "resolve"; | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
}>, import("valibot").ObjectSchema<{ | ||
@@ -236,4 +236,4 @@ type: import("valibot").LiteralSchema<"sign-personal-message", "sign-personal-message">; | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>], { | ||
@@ -244,8 +244,8 @@ address: string; | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
} | { | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>; | ||
@@ -258,8 +258,8 @@ }, undefined, { | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
} | { | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}; | ||
@@ -275,8 +275,8 @@ type: "resolve"; | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
} | { | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}; | ||
@@ -295,8 +295,8 @@ type: "resolve"; | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
} | { | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}; | ||
@@ -303,0 +303,0 @@ type: "resolve"; |
@@ -31,4 +31,4 @@ import type { Output } from 'valibot'; | ||
address: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}; | ||
@@ -35,0 +35,0 @@ name?: string | undefined; |
export { ZkSendLinkBuilder, type ZkSendLinkBuilderOptions, type CreateZkSendLinkOptions, } from './links/builder.js'; | ||
export { ZkSendLink, type ZkSendLinkOptions } from './links/claim.js'; | ||
export { type ZkBagContractOptions } from './links/zk-bag.js'; | ||
export { listCreatedLinks, isClaimTransaction } from './links/utils.js'; | ||
export { type ZkBagContractOptions, ZkBag } from './links/zk-bag.js'; | ||
export { isClaimTransaction } from './links/utils.js'; | ||
export { listCreatedLinks } from './links/list-created-links.js'; | ||
export { MAINNET_CONTRACT_IDS } from './links/zk-bag.js'; | ||
export * from './wallet.js'; | ||
export * from './channel/index.js'; |
@@ -22,6 +22,8 @@ "use strict"; | ||
__export(src_exports, { | ||
MAINNET_CONTRACT_IDS: () => import_zk_bag2.MAINNET_CONTRACT_IDS, | ||
ZkBag: () => import_zk_bag.ZkBag, | ||
ZkSendLink: () => import_claim.ZkSendLink, | ||
ZkSendLinkBuilder: () => import_builder.ZkSendLinkBuilder, | ||
isClaimTransaction: () => import_utils.isClaimTransaction, | ||
listCreatedLinks: () => import_utils.listCreatedLinks | ||
listCreatedLinks: () => import_list_created_links.listCreatedLinks | ||
}); | ||
@@ -33,4 +35,6 @@ module.exports = __toCommonJS(src_exports); | ||
var import_utils = require("./links/utils.js"); | ||
var import_list_created_links = require("./links/list-created-links.js"); | ||
var import_zk_bag2 = require("./links/zk-bag.js"); | ||
__reExport(src_exports, require("./wallet.js"), module.exports); | ||
__reExport(src_exports, require("./channel/index.js"), module.exports); | ||
//# sourceMappingURL=index.js.map |
@@ -18,3 +18,3 @@ import { SuiClient } from '@mysten/sui.js/client'; | ||
redirect?: ZkSendLinkRedirect; | ||
contract?: ZkBagContractOptions; | ||
contract?: ZkBagContractOptions | null; | ||
} | ||
@@ -31,2 +31,6 @@ export interface CreateZkSendLinkOptions { | ||
#private; | ||
objectIds: Set<string>; | ||
balances: Map<string, bigint>; | ||
sender: string; | ||
keypair: Keypair; | ||
constructor({ host, path, keypair, network, client, sender, redirect, contract, }: ZkSendLinkBuilderOptions); | ||
@@ -41,4 +45,11 @@ addClaimableMist(amount: bigint): void; | ||
}): Promise<import("@mysten/sui.js/client").SuiTransactionBlockResponse>; | ||
createSendTransaction({ transactionBlock: txb, calculateGas, }?: CreateZkSendLinkOptions): Promise<TransactionBlock>; | ||
createSendTransaction({ transactionBlock, calculateGas, }?: CreateZkSendLinkOptions): Promise<TransactionBlock>; | ||
static createLinks({ links, network, client, transactionBlock, contract: contractIds, }: { | ||
transactionBlock?: TransactionBlock; | ||
client?: SuiClient; | ||
network?: 'mainnet' | 'testnet'; | ||
links: ZkSendLinkBuilder[]; | ||
contract: ZkBagContractOptions; | ||
}): Promise<TransactionBlock>; | ||
} | ||
export {}; |
@@ -52,3 +52,3 @@ "use strict"; | ||
var import_zk_bag = require("./zk-bag.js"); | ||
var _host, _path, _keypair, _client, _redirect, _objects, _balances, _sender, _coinsByType, _contract, _objectsToTransfer, objectsToTransfer_fn, _createSendTransactionWithoutContract, createSendTransactionWithoutContract_fn, _estimateClaimGasFee, estimateClaimGasFee_fn, _getCoinsByType, getCoinsByType_fn; | ||
var _host, _path, _client, _redirect, _coinsByType, _contract, _objectsToTransfer, objectsToTransfer_fn, _createSendTransactionWithoutContract, createSendTransactionWithoutContract_fn, _estimateClaimGasFee, estimateClaimGasFee_fn, _getCoinsByType, getCoinsByType_fn; | ||
const DEFAULT_ZK_SEND_LINK_OPTIONS = { | ||
@@ -60,3 +60,3 @@ host: "https://zksend.com", | ||
const SUI_COIN_TYPE = (0, import_utils.normalizeStructTag)(import_utils.SUI_TYPE_ARG); | ||
class ZkSendLinkBuilder { | ||
const _ZkSendLinkBuilder = class { | ||
constructor({ | ||
@@ -70,3 +70,3 @@ host = DEFAULT_ZK_SEND_LINK_OPTIONS.host, | ||
redirect, | ||
contract | ||
contract = network === "mainnet" ? import_zk_bag.MAINNET_CONTRACT_IDS : void 0 | ||
}) { | ||
@@ -77,10 +77,8 @@ __privateAdd(this, _objectsToTransfer); | ||
__privateAdd(this, _getCoinsByType); | ||
this.objectIds = /* @__PURE__ */ new Set(); | ||
this.balances = /* @__PURE__ */ new Map(); | ||
__privateAdd(this, _host, void 0); | ||
__privateAdd(this, _path, void 0); | ||
__privateAdd(this, _keypair, void 0); | ||
__privateAdd(this, _client, void 0); | ||
__privateAdd(this, _redirect, void 0); | ||
__privateAdd(this, _objects, /* @__PURE__ */ new Set()); | ||
__privateAdd(this, _balances, /* @__PURE__ */ new Map()); | ||
__privateAdd(this, _sender, void 0); | ||
__privateAdd(this, _coinsByType, /* @__PURE__ */ new Map()); | ||
@@ -91,5 +89,5 @@ __privateAdd(this, _contract, void 0); | ||
__privateSet(this, _redirect, redirect); | ||
__privateSet(this, _keypair, keypair); | ||
this.keypair = keypair; | ||
__privateSet(this, _client, client); | ||
__privateSet(this, _sender, (0, import_utils.normalizeSuiAddress)(sender)); | ||
this.sender = (0, import_utils.normalizeSuiAddress)(sender); | ||
if (contract) { | ||
@@ -104,6 +102,6 @@ __privateSet(this, _contract, new import_zk_bag.ZkBag(contract.packageId, contract)); | ||
const normalizedType = (0, import_utils.normalizeStructTag)(coinType); | ||
__privateGet(this, _balances).set(normalizedType, (__privateGet(this, _balances).get(normalizedType) ?? 0n) + amount); | ||
this.balances.set(normalizedType, (this.balances.get(normalizedType) ?? 0n) + amount); | ||
} | ||
addClaimableObject(id) { | ||
__privateGet(this, _objects).add(id); | ||
this.objectIds.add(id); | ||
} | ||
@@ -114,3 +112,3 @@ getLink() { | ||
link.hash = `${__privateGet(this, _contract) ? "$" : ""}${(0, import_utils.toB64)( | ||
(0, import_cryptography.decodeSuiPrivateKey)(__privateGet(this, _keypair).getSecretKey()).secretKey | ||
(0, import_cryptography.decodeSuiPrivateKey)(this.keypair.getSecretKey()).secretKey | ||
)}`; | ||
@@ -140,30 +138,120 @@ if (__privateGet(this, _redirect)) { | ||
async createSendTransaction({ | ||
transactionBlock: txb = new import_transactions.TransactionBlock(), | ||
transactionBlock = new import_transactions.TransactionBlock(), | ||
calculateGas | ||
} = {}) { | ||
if (!__privateGet(this, _contract)) { | ||
return __privateMethod(this, _createSendTransactionWithoutContract, createSendTransactionWithoutContract_fn).call(this, { transactionBlock: txb, calculateGas }); | ||
return __privateMethod(this, _createSendTransactionWithoutContract, createSendTransactionWithoutContract_fn).call(this, { transactionBlock, calculateGas }); | ||
} | ||
const receiver = txb.pure.address(__privateGet(this, _keypair).toSuiAddress()); | ||
const store = txb.object(__privateGet(this, _contract).ids.bagStoreId); | ||
__privateGet(this, _contract).new(txb, { arguments: [store, receiver] }); | ||
txb.setSenderIfNotSet(__privateGet(this, _sender)); | ||
const objectsToTransfer = await __privateMethod(this, _objectsToTransfer, objectsToTransfer_fn).call(this, txb); | ||
for (const object of objectsToTransfer) { | ||
__privateGet(this, _contract).add(txb, { | ||
arguments: [store, receiver, object.ref], | ||
typeArguments: [object.type] | ||
transactionBlock.setSenderIfNotSet(this.sender); | ||
return _ZkSendLinkBuilder.createLinks({ | ||
transactionBlock, | ||
client: __privateGet(this, _client), | ||
contract: __privateGet(this, _contract).ids, | ||
links: [this] | ||
}); | ||
} | ||
static async createLinks({ | ||
links, | ||
network = "mainnet", | ||
client = new import_client.SuiClient({ url: (0, import_client.getFullnodeUrl)(network) }), | ||
transactionBlock = new import_transactions.TransactionBlock(), | ||
contract: contractIds = import_zk_bag.MAINNET_CONTRACT_IDS | ||
}) { | ||
const contract = new import_zk_bag.ZkBag(contractIds.packageId, contractIds); | ||
const store = transactionBlock.object(contract.ids.bagStoreId); | ||
const coinsByType = /* @__PURE__ */ new Map(); | ||
const allIds = links.flatMap((link) => [...link.objectIds]); | ||
await Promise.all( | ||
[...new Set(links.flatMap((link) => [...link.balances.keys()]))].map(async (coinType) => { | ||
const coins = await client.getCoins({ | ||
coinType, | ||
owner: links[0].sender | ||
}); | ||
coinsByType.set( | ||
coinType, | ||
coins.data.filter((coin) => !allIds.includes(coin.coinObjectId)) | ||
); | ||
}) | ||
); | ||
const objectRefs = /* @__PURE__ */ new Map(); | ||
const pageSize = 50; | ||
let offset = 0; | ||
while (offset < allIds.length) { | ||
let chunk = allIds.slice(offset, offset + pageSize); | ||
offset += pageSize; | ||
const objects = await client.multiGetObjects({ | ||
ids: chunk, | ||
options: { | ||
showType: true | ||
} | ||
}); | ||
for (const [i, res] of objects.entries()) { | ||
if (!res.data || res.error) { | ||
throw new Error(`Failed to load object ${chunk[i]} (${res.error?.code})`); | ||
} | ||
objectRefs.set(chunk[i], { | ||
ref: transactionBlock.objectRef({ | ||
version: res.data.version, | ||
digest: res.data.digest, | ||
objectId: res.data.objectId | ||
}), | ||
type: res.data.type | ||
}); | ||
} | ||
} | ||
return txb; | ||
const mergedCoins = /* @__PURE__ */ new Map([ | ||
[SUI_COIN_TYPE, transactionBlock.gas] | ||
]); | ||
for (const [coinType, coins] of coinsByType) { | ||
if (coinType === SUI_COIN_TYPE) { | ||
continue; | ||
} | ||
const [first, ...rest] = coins.map( | ||
(coin) => transactionBlock.objectRef({ | ||
objectId: coin.coinObjectId, | ||
version: coin.version, | ||
digest: coin.digest | ||
}) | ||
); | ||
if (rest.length > 0) { | ||
transactionBlock.mergeCoins(first, rest); | ||
} | ||
mergedCoins.set(coinType, transactionBlock.object(first)); | ||
} | ||
for (const link of links) { | ||
const receiver = link.keypair.toSuiAddress(); | ||
contract.new(transactionBlock, { arguments: [store, receiver] }); | ||
link.objectIds.forEach((id) => { | ||
const object = objectRefs.get(id); | ||
if (!object) { | ||
throw new Error(`Object ${id} not found`); | ||
} | ||
contract.add(transactionBlock, { | ||
arguments: [store, receiver, object.ref], | ||
typeArguments: [object.type] | ||
}); | ||
}); | ||
} | ||
for (const [coinType, merged] of mergedCoins) { | ||
const linksWithCoin = links.filter((link) => link.balances.has(coinType)); | ||
if (linksWithCoin.length === 0) { | ||
continue; | ||
} | ||
const balances = linksWithCoin.map((link) => link.balances.get(coinType)); | ||
const splits = transactionBlock.splitCoins(merged, balances); | ||
for (const [i, link] of linksWithCoin.entries()) { | ||
contract.add(transactionBlock, { | ||
arguments: [store, link.keypair.toSuiAddress(), splits[i]], | ||
typeArguments: [`0x2::coin::Coin<${coinType}>`] | ||
}); | ||
} | ||
} | ||
return transactionBlock; | ||
} | ||
} | ||
}; | ||
let ZkSendLinkBuilder = _ZkSendLinkBuilder; | ||
_host = new WeakMap(); | ||
_path = new WeakMap(); | ||
_keypair = new WeakMap(); | ||
_client = new WeakMap(); | ||
_redirect = new WeakMap(); | ||
_objects = new WeakMap(); | ||
_balances = new WeakMap(); | ||
_sender = new WeakMap(); | ||
_coinsByType = new WeakMap(); | ||
@@ -173,3 +261,3 @@ _contract = new WeakMap(); | ||
objectsToTransfer_fn = async function(txb) { | ||
const objectIDs = [...__privateGet(this, _objects)]; | ||
const objectIDs = [...this.objectIds]; | ||
const refsWithType = (await __privateGet(this, _client).multiGetObjects({ | ||
@@ -193,4 +281,4 @@ ids: objectIDs, | ||
}); | ||
txb.setSenderIfNotSet(__privateGet(this, _sender)); | ||
for (const [coinType, amount] of __privateGet(this, _balances)) { | ||
txb.setSenderIfNotSet(this.sender); | ||
for (const [coinType, amount] of this.balances) { | ||
if (coinType === SUI_COIN_TYPE) { | ||
@@ -223,4 +311,4 @@ const [sui] = txb.splitCoins(txb.gas, [amount]); | ||
const baseGasAmount = calculateGas ? await calculateGas({ | ||
balances: __privateGet(this, _balances), | ||
objects: [...__privateGet(this, _objects)], | ||
balances: this.balances, | ||
objects: [...this.objectIds], | ||
gasEstimateFromDryRun | ||
@@ -230,7 +318,7 @@ }) : gasEstimateFromDryRun * 2n; | ||
const roundedGasAmount = gasWithBuffer - gasWithBuffer % 1000n - 13n; | ||
const address = __privateGet(this, _keypair).toSuiAddress(); | ||
const address = this.keypair.toSuiAddress(); | ||
const objectsToTransfer = (await __privateMethod(this, _objectsToTransfer, objectsToTransfer_fn).call(this, txb)).map((obj) => obj.ref); | ||
const [gas] = txb.splitCoins(txb.gas, [roundedGasAmount]); | ||
objectsToTransfer.push(gas); | ||
txb.setSenderIfNotSet(__privateGet(this, _sender)); | ||
txb.setSenderIfNotSet(this.sender); | ||
txb.transferObjects(objectsToTransfer, address); | ||
@@ -242,7 +330,7 @@ return txb; | ||
const txb = new import_transactions.TransactionBlock(); | ||
txb.setSender(__privateGet(this, _sender)); | ||
txb.setSender(this.sender); | ||
txb.setGasPayment([]); | ||
txb.transferObjects([txb.gas], __privateGet(this, _keypair).toSuiAddress()); | ||
const idsToTransfer = [...__privateGet(this, _objects)]; | ||
for (const [coinType] of __privateGet(this, _balances)) { | ||
txb.transferObjects([txb.gas], this.keypair.toSuiAddress()); | ||
const idsToTransfer = [...this.objectIds]; | ||
for (const [coinType] of this.balances) { | ||
const coins = await __privateMethod(this, _getCoinsByType, getCoinsByType_fn).call(this, coinType); | ||
@@ -257,3 +345,3 @@ if (!coins.length) { | ||
idsToTransfer.map((id) => txb.object(id)), | ||
__privateGet(this, _keypair).toSuiAddress() | ||
this.keypair.toSuiAddress() | ||
); | ||
@@ -273,3 +361,3 @@ } | ||
coinType, | ||
owner: __privateGet(this, _sender) | ||
owner: this.sender | ||
}); | ||
@@ -276,0 +364,0 @@ __privateGet(this, _coinsByType).set(coinType, coins.data); |
@@ -5,2 +5,3 @@ import { SuiClient } from '@mysten/sui.js/client'; | ||
import type { ZkSendLinkBuilderOptions } from './builder.js'; | ||
import type { LinkAssets } from './utils.js'; | ||
import type { ZkBagContractOptions } from './zk-bag.js'; | ||
@@ -15,2 +16,4 @@ export type ZkSendLinkOptions = { | ||
address?: string; | ||
isContractLink: boolean; | ||
contract?: ZkBagContractOptions | null; | ||
} & ({ | ||
@@ -22,8 +25,2 @@ address: string; | ||
address?: never; | ||
}) & ({ | ||
isContractLink: true; | ||
contract: ZkBagContractOptions; | ||
} | { | ||
isContractLink: false; | ||
contract?: never; | ||
}); | ||
@@ -35,44 +32,10 @@ export declare class ZkSendLink { | ||
creatorAddress?: string; | ||
assets?: LinkAssets; | ||
claimed?: boolean; | ||
constructor({ network, claimApi, client, keypair, contract, address, host, path, isContractLink, }: ZkSendLinkOptions); | ||
static fromUrl(url: string, { contract, ...options }?: Omit<ZkSendLinkOptions, 'keypair' | 'address' | 'isContractLink'>): Promise<ZkSendLink>; | ||
static fromAddress(address: string, options: Omit<ZkSendLinkOptions, 'keypair' | 'address' | 'isContractLink'> & { | ||
contract: ZkBagContractOptions; | ||
}): Promise<ZkSendLink>; | ||
loadOwnedData(): Promise<void>; | ||
listClaimableAssets(address: string, options?: { | ||
claimObjectsAddedAfterCreation?: boolean; | ||
coinTypes?: string[]; | ||
objects?: string[]; | ||
}): Promise<{ | ||
balances: { | ||
coinType: string; | ||
amount: bigint; | ||
}[]; | ||
nfts: { | ||
objectId: string; | ||
type: string; | ||
version: string; | ||
digest: string; | ||
}[]; | ||
coins: { | ||
objectId: string; | ||
type: string; | ||
version: string; | ||
digest: string; | ||
}[]; | ||
}>; | ||
claimAssets(address: string, | ||
/** @deprecated filtering claims is not supported in contract based links */ | ||
options?: { | ||
claimObjectsAddedAfterCreation?: boolean; | ||
coinTypes?: string[]; | ||
objects?: string[]; | ||
}): Promise<import("@mysten/sui.js/client").SuiTransactionBlockResponse>; | ||
createClaimTransaction(address: string, { reclaim, ...options }?: { | ||
/** @deprecated filtering claims is not supported in contract based links */ | ||
claimObjectsAddedAfterCreation?: boolean; | ||
/** @deprecated filtering claims is not supported in contract based links */ | ||
coinTypes?: string[]; | ||
/** @deprecated filtering claims is not supported in contract based links */ | ||
objects?: string[]; | ||
static fromUrl(url: string, options?: Omit<ZkSendLinkOptions, 'keypair' | 'address' | 'isContractLink'>): Promise<ZkSendLink>; | ||
static fromAddress(address: string, options: Omit<ZkSendLinkOptions, 'keypair' | 'address' | 'isContractLink'>): Promise<ZkSendLink>; | ||
loadAssets(): Promise<void>; | ||
claimAssets(address: string): Promise<import("@mysten/sui.js/client").SuiTransactionBlockResponse>; | ||
createClaimTransaction(address: string, { reclaim, }?: { | ||
reclaim?: boolean; | ||
@@ -79,0 +42,0 @@ }): TransactionBlock; |
@@ -46,2 +46,3 @@ "use strict"; | ||
module.exports = __toCommonJS(claim_exports); | ||
var import_bcs = require("@mysten/sui.js/bcs"); | ||
var import_client = require("@mysten/sui.js/client"); | ||
@@ -52,4 +53,5 @@ var import_ed25519 = require("@mysten/sui.js/keypairs/ed25519"); | ||
var import_builder = require("./builder.js"); | ||
var import_utils2 = require("./utils.js"); | ||
var import_zk_bag = require("./zk-bag.js"); | ||
var _client, _initiallyOwnedObjects, _ownedObjects, _bagObjects, _gasCoin, _hasSui, _contract, _claimApi, _network, _host, _path, _loadBag, loadBag_fn, _createSponsoredTransactionBlock, createSponsoredTransactionBlock_fn, _executeSponsoredTransactionBlock, executeSponsoredTransactionBlock_fn, _fetch, fetch_fn, _listNonContractClaimableAssets, listNonContractClaimableAssets_fn, _createNonContractClaimTransaction, createNonContractClaimTransaction_fn, _loadOwnedObjects, loadOwnedObjects_fn, _loadInitialTransactionData, loadInitialTransactionData_fn; | ||
var _client, _contract, _claimApi, _network, _host, _path, _gasCoin, _hasSui, _ownedObjects, _loadBag, loadBag_fn, _loadClaimedAssets, loadClaimedAssets_fn, _createSponsoredTransactionBlock, createSponsoredTransactionBlock_fn, _executeSponsoredTransactionBlock, executeSponsoredTransactionBlock_fn, _fetch, fetch_fn, _listNonContractClaimableAssets, listNonContractClaimableAssets_fn, _createNonContractClaimTransaction, createNonContractClaimTransaction_fn, _loadOwnedObjects, loadOwnedObjects_fn; | ||
const DEFAULT_ZK_SEND_LINK_OPTIONS = { | ||
@@ -69,3 +71,3 @@ host: "https://zksend.com", | ||
keypair, | ||
contract, | ||
contract = network === "mainnet" ? import_zk_bag.MAINNET_CONTRACT_IDS : null, | ||
address, | ||
@@ -77,2 +79,3 @@ host, | ||
__privateAdd(this, _loadBag); | ||
__privateAdd(this, _loadClaimedAssets); | ||
__privateAdd(this, _createSponsoredTransactionBlock); | ||
@@ -84,9 +87,3 @@ __privateAdd(this, _executeSponsoredTransactionBlock); | ||
__privateAdd(this, _loadOwnedObjects); | ||
__privateAdd(this, _loadInitialTransactionData); | ||
__privateAdd(this, _client, void 0); | ||
__privateAdd(this, _initiallyOwnedObjects, /* @__PURE__ */ new Set()); | ||
__privateAdd(this, _ownedObjects, []); | ||
__privateAdd(this, _bagObjects, null); | ||
__privateAdd(this, _gasCoin, void 0); | ||
__privateAdd(this, _hasSui, false); | ||
__privateAdd(this, _contract, void 0); | ||
@@ -97,2 +94,6 @@ __privateAdd(this, _claimApi, void 0); | ||
__privateAdd(this, _path, void 0); | ||
// State for non-contract based links | ||
__privateAdd(this, _gasCoin, void 0); | ||
__privateAdd(this, _hasSui, false); | ||
__privateAdd(this, _ownedObjects, []); | ||
if (!keypair && !address) { | ||
@@ -115,6 +116,3 @@ throw new Error("Either keypair or address must be provided"); | ||
} | ||
static async fromUrl(url, { | ||
contract, | ||
...options | ||
} = {}) { | ||
static async fromUrl(url, options = {}) { | ||
const parsed = new URL(url); | ||
@@ -124,5 +122,2 @@ const isContractLink = parsed.hash.startsWith("#$"); | ||
if (isContractLink) { | ||
if (!contract) { | ||
throw new Error("Contract options are required for contract based links"); | ||
} | ||
const keypair = import_ed25519.Ed25519Keypair.fromSecretKey((0, import_utils.fromB64)(parsed.hash.slice(2))); | ||
@@ -134,4 +129,3 @@ link = new _ZkSendLink({ | ||
path: parsed.pathname, | ||
isContractLink: true, | ||
contract | ||
isContractLink: true | ||
}); | ||
@@ -150,3 +144,3 @@ } else { | ||
} | ||
await link.loadOwnedData(); | ||
await link.loadAssets(); | ||
return link; | ||
@@ -160,56 +154,29 @@ } | ||
}); | ||
await link.loadOwnedData(); | ||
await link.loadAssets(); | ||
return link; | ||
} | ||
async loadOwnedData() { | ||
async loadAssets() { | ||
if (__privateGet(this, _contract)) { | ||
await __privateMethod(this, _loadBag, loadBag_fn).call(this); | ||
} else { | ||
await Promise.all([__privateMethod(this, _loadInitialTransactionData, loadInitialTransactionData_fn).call(this), __privateMethod(this, _loadOwnedObjects, loadOwnedObjects_fn).call(this)]); | ||
await __privateMethod(this, _loadOwnedObjects, loadOwnedObjects_fn).call(this); | ||
} | ||
} | ||
async listClaimableAssets(address, options) { | ||
if (!__privateGet(this, _contract)) { | ||
return __privateMethod(this, _listNonContractClaimableAssets, listNonContractClaimableAssets_fn).call(this, address, options); | ||
} | ||
const coins = []; | ||
const nfts = []; | ||
for (const object of __privateGet(this, _bagObjects) ?? []) { | ||
const type = (0, import_utils.parseStructTag)(object.type); | ||
if (type.address === (0, import_utils.normalizeSuiAddress)("0x2") && type.module === "coin" && type.name === "Coin") { | ||
coins.push(object); | ||
} else { | ||
nfts.push(object); | ||
} | ||
} | ||
const balances = /* @__PURE__ */ new Map(); | ||
coins.forEach((coin) => { | ||
if (coin.content?.dataType !== "moveObject") { | ||
return; | ||
} | ||
const amount = BigInt(coin.content.fields.balance); | ||
const coinType = (0, import_utils.normalizeStructTag)((0, import_utils.parseStructTag)(coin.content.type).typeParams[0]); | ||
if (!balances.has(coinType)) { | ||
balances.set(coinType, { coinType, amount }); | ||
} else { | ||
balances.get(coinType).amount += amount; | ||
} | ||
}); | ||
return { | ||
balances: [...balances.values()], | ||
nfts, | ||
coins | ||
}; | ||
} | ||
async claimAssets(address, options) { | ||
async claimAssets(address) { | ||
if (!this.keypair) { | ||
throw new Error("Cannot claim assets without links keypair"); | ||
} | ||
const txb = this.createClaimTransaction(address, options); | ||
if (!__privateGet(this, _contract) || !__privateGet(this, _bagObjects)) { | ||
if (this.claimed) { | ||
throw new Error("Assets have already been claimed"); | ||
} | ||
if (!__privateGet(this, _contract)) { | ||
return __privateGet(this, _client).signAndExecuteTransactionBlock({ | ||
transactionBlock: txb, | ||
transactionBlock: this.createClaimTransaction(address), | ||
signer: this.keypair | ||
}); | ||
} | ||
if (!this.assets) { | ||
await __privateMethod(this, _loadBag, loadBag_fn).call(this); | ||
} | ||
const txb = this.createClaimTransaction(address); | ||
const { digest } = await __privateMethod(this, _executeSponsoredTransactionBlock, executeSponsoredTransactionBlock_fn).call(this, await __privateMethod(this, _createSponsoredTransactionBlock, createSponsoredTransactionBlock_fn).call(this, txb, address, this.keypair.toSuiAddress()), this.keypair); | ||
@@ -219,11 +186,7 @@ return __privateGet(this, _client).waitForTransactionBlock({ digest }); | ||
createClaimTransaction(address, { | ||
reclaim, | ||
...options | ||
reclaim | ||
} = {}) { | ||
if (!__privateGet(this, _contract)) { | ||
return __privateMethod(this, _createNonContractClaimTransaction, createNonContractClaimTransaction_fn).call(this, address, options); | ||
return __privateMethod(this, _createNonContractClaimTransaction, createNonContractClaimTransaction_fn).call(this, address); | ||
} | ||
if (Object.keys(options).length > 0) { | ||
throw new Error("Filtering claims is not supported for contract based links"); | ||
} | ||
if (!this.keypair && !reclaim) { | ||
@@ -238,3 +201,4 @@ throw new Error("Cannot claim assets without the links keypair"); | ||
const objectsToTransfer = []; | ||
for (const object of __privateGet(this, _bagObjects) ?? []) { | ||
const objects = [...this.assets?.coins ?? [], ...this.assets?.nfts ?? []]; | ||
for (const object of objects) { | ||
objectsToTransfer.push( | ||
@@ -262,3 +226,9 @@ __privateGet(this, _contract).claim(txb, { | ||
async createRegenerateTransaction(sender, options = {}) { | ||
if (!__privateGet(this, _contract) || !__privateGet(this, _bagObjects)) { | ||
if (!this.assets) { | ||
await __privateMethod(this, _loadBag, loadBag_fn).call(this); | ||
} | ||
if (this.claimed) { | ||
throw new Error("Assets have already been claimed"); | ||
} | ||
if (!__privateGet(this, _contract)) { | ||
throw new Error("Regenerating non-contract based links is not supported"); | ||
@@ -289,7 +259,2 @@ } | ||
_client = new WeakMap(); | ||
_initiallyOwnedObjects = new WeakMap(); | ||
_ownedObjects = new WeakMap(); | ||
_bagObjects = new WeakMap(); | ||
_gasCoin = new WeakMap(); | ||
_hasSui = new WeakMap(); | ||
_contract = new WeakMap(); | ||
@@ -300,2 +265,5 @@ _claimApi = new WeakMap(); | ||
_path = new WeakMap(); | ||
_gasCoin = new WeakMap(); | ||
_hasSui = new WeakMap(); | ||
_ownedObjects = new WeakMap(); | ||
_loadBag = new WeakSet(); | ||
@@ -306,2 +274,7 @@ loadBag_fn = async function() { | ||
} | ||
this.assets = { | ||
balances: [], | ||
nfts: [], | ||
coins: [] | ||
}; | ||
const bagField = await __privateGet(this, _client).getDynamicFieldObject({ | ||
@@ -315,3 +288,7 @@ parentId: __privateGet(this, _contract).ids.bagStoreTableId, | ||
if (!bagField.data) { | ||
this.claimed = true; | ||
await __privateMethod(this, _loadClaimedAssets, loadClaimedAssets_fn).call(this); | ||
return; | ||
} else { | ||
this.claimed = false; | ||
} | ||
@@ -330,15 +307,78 @@ const itemIds = bagField.data?.content?.fields?.value?.fields?.item_ids.fields.contents; | ||
}); | ||
__privateSet(this, _bagObjects, objectsResponse.map((object, i) => { | ||
if (!object.data) { | ||
const balances = /* @__PURE__ */ new Map(); | ||
objectsResponse.forEach((object, i) => { | ||
if (!object.data || !object.data.type) { | ||
throw new Error(`Failed to load claimable object ${itemIds[i]}`); | ||
} | ||
return { | ||
objectId: object.data.objectId, | ||
type: (0, import_utils.normalizeStructTag)(object.data.type), | ||
version: object.data.version, | ||
digest: object.data.digest, | ||
content: object.data.content | ||
}; | ||
})); | ||
const type = (0, import_utils.parseStructTag)((0, import_utils.normalizeStructTag)(object.data.type)); | ||
if (type.address === (0, import_utils.normalizeSuiAddress)("0x2") && type.module === "coin" && type.name === "Coin") { | ||
this.assets.coins.push({ | ||
objectId: object.data.objectId, | ||
type: object.data.type, | ||
version: object.data.version, | ||
digest: object.data.digest | ||
}); | ||
if (object.data.content?.dataType === "moveObject") { | ||
const amount = BigInt(object.data.content.fields.balance); | ||
const coinType = (0, import_utils.normalizeStructTag)( | ||
(0, import_utils.parseStructTag)(object.data.content.type).typeParams[0] | ||
); | ||
if (!balances.has(coinType)) { | ||
balances.set(coinType, { coinType, amount }); | ||
} else { | ||
balances.get(coinType).amount += amount; | ||
} | ||
} | ||
} else { | ||
this.assets.nfts.push({ | ||
objectId: object.data.objectId, | ||
type: object.data.type, | ||
version: object.data.version, | ||
digest: object.data.digest | ||
}); | ||
} | ||
}); | ||
this.assets.balances = [...balances.values()]; | ||
}; | ||
_loadClaimedAssets = new WeakSet(); | ||
loadClaimedAssets_fn = async function() { | ||
const result = await __privateGet(this, _client).queryTransactionBlocks({ | ||
limit: 1, | ||
filter: { | ||
FromAddress: this.address | ||
}, | ||
options: { | ||
showObjectChanges: true, | ||
showBalanceChanges: true, | ||
showInput: true | ||
} | ||
}); | ||
if (!result?.data[0]) { | ||
return; | ||
} | ||
const [txb] = result.data; | ||
if (txb.transaction?.data.transaction.kind !== "ProgrammableTransaction") { | ||
return; | ||
} | ||
const transfer = txb.transaction.data.transaction.transactions.findLast( | ||
(tx) => "TransferObjects" in tx | ||
); | ||
if (!transfer) { | ||
return; | ||
} | ||
const receiverArg = transfer.TransferObjects[1]; | ||
if (!(typeof receiverArg === "object" && "Input" in receiverArg)) { | ||
return; | ||
} | ||
const input = txb.transaction.data.transaction.inputs[receiverArg.Input]; | ||
if (input.type !== "pure") { | ||
return; | ||
} | ||
const receiver = typeof input.value === "string" ? input.value : import_bcs.bcs.Address.parse(new Uint8Array(input.value.Pure)); | ||
this.assets = (0, import_utils2.getAssetsFromTxnBlock)({ | ||
transactionBlock: txb, | ||
address: receiver, | ||
isSent: false | ||
}); | ||
}; | ||
_createSponsoredTransactionBlock = new WeakSet(); | ||
@@ -394,5 +434,16 @@ createSponsoredTransactionBlock_fn = async function(txb, claimer, sender) { | ||
_listNonContractClaimableAssets = new WeakSet(); | ||
listNonContractClaimableAssets_fn = async function(address, options) { | ||
listNonContractClaimableAssets_fn = async function() { | ||
const balances = []; | ||
const nfts = []; | ||
const coins = []; | ||
if (__privateGet(this, _ownedObjects).length === 0 && !__privateGet(this, _hasSui)) { | ||
return { | ||
balances, | ||
nfts, | ||
coins | ||
}; | ||
} | ||
const address = new import_ed25519.Ed25519Keypair().toSuiAddress(); | ||
const normalizedAddress = (0, import_utils.normalizeSuiAddress)(address); | ||
const txb = this.createClaimTransaction(normalizedAddress, options); | ||
const txb = this.createClaimTransaction(normalizedAddress); | ||
if (__privateGet(this, _gasCoin) || !__privateGet(this, _hasSui)) { | ||
@@ -404,7 +455,4 @@ txb.setGasPayment([]); | ||
}); | ||
const balances = []; | ||
const nfts = []; | ||
const coins = []; | ||
dryRun.balanceChanges.forEach((balanceChange) => { | ||
if (BigInt(balanceChange.amount) > 0n && isOwner(balanceChange.owner, normalizedAddress)) { | ||
if (BigInt(balanceChange.amount) > 0n && (0, import_utils2.isOwner)(balanceChange.owner, normalizedAddress)) { | ||
balances.push({ | ||
@@ -420,3 +468,3 @@ coinType: (0, import_utils.normalizeStructTag)(balanceChange.coinType), | ||
if (type.address === (0, import_utils.normalizeSuiAddress)("0x2") && type.module === "coin" && type.name === "Coin") { | ||
if (ownedAfterChange(objectChange, normalizedAddress)) { | ||
if ((0, import_utils2.ownedAfterChange)(objectChange, normalizedAddress)) { | ||
coins.push(objectChange); | ||
@@ -427,3 +475,3 @@ } | ||
} | ||
if (ownedAfterChange(objectChange, normalizedAddress)) { | ||
if ((0, import_utils2.ownedAfterChange)(objectChange, normalizedAddress)) { | ||
nfts.push(objectChange); | ||
@@ -439,12 +487,8 @@ } | ||
_createNonContractClaimTransaction = new WeakSet(); | ||
createNonContractClaimTransaction_fn = function(address, options) { | ||
createNonContractClaimTransaction_fn = function(address) { | ||
if (!this.keypair) { | ||
throw new Error("Cannot claim assets without the links keypair"); | ||
} | ||
const claimAll = !options?.coinTypes && !options?.objects; | ||
const txb = new import_transactions.TransactionBlock(); | ||
txb.setSender(this.keypair.toSuiAddress()); | ||
const coinTypes = new Set( | ||
options?.coinTypes?.map((type) => (0, import_utils.normalizeStructTag)(`0x2::coin::Coin<${type}>`)) ?? [] | ||
); | ||
const objectsToTransfer = __privateGet(this, _ownedObjects).filter((object) => { | ||
@@ -458,13 +502,7 @@ if (__privateGet(this, _gasCoin)) { | ||
} | ||
if (coinTypes?.has(object.type) || options?.objects?.includes(object.objectId)) { | ||
return true; | ||
} | ||
if (!options?.claimObjectsAddedAfterCreation && !__privateGet(this, _initiallyOwnedObjects).has(object.objectId)) { | ||
return false; | ||
} | ||
return claimAll; | ||
return true; | ||
}).map((object) => txb.object(object.objectId)); | ||
if (__privateGet(this, _gasCoin) && this.creatorAddress) { | ||
txb.transferObjects([txb.gas], this.creatorAddress); | ||
} else if (claimAll || coinTypes?.has(SUI_COIN_TYPE)) { | ||
} else { | ||
objectsToTransfer.push(txb.gas); | ||
@@ -479,3 +517,7 @@ } | ||
loadOwnedObjects_fn = async function() { | ||
__privateSet(this, _ownedObjects, []); | ||
this.assets = { | ||
nfts: [], | ||
balances: [], | ||
coins: [] | ||
}; | ||
let nextCursor; | ||
@@ -487,3 +529,4 @@ do { | ||
options: { | ||
showType: true | ||
showType: true, | ||
showContent: true | ||
} | ||
@@ -509,5 +552,2 @@ }); | ||
__privateSet(this, _gasCoin, coins.data.find((coin) => BigInt(coin.balance) % 1000n === 987n)); | ||
}; | ||
_loadInitialTransactionData = new WeakSet(); | ||
loadInitialTransactionData_fn = async function() { | ||
const result = await __privateGet(this, _client).queryTransactionBlocks({ | ||
@@ -520,25 +560,16 @@ limit: 1, | ||
options: { | ||
showObjectChanges: true, | ||
showInput: true | ||
showInput: true, | ||
showBalanceChanges: true, | ||
showObjectChanges: true | ||
} | ||
}); | ||
result.data[0]?.objectChanges?.forEach((objectChange) => { | ||
if (ownedAfterChange(objectChange, this.address)) { | ||
__privateGet(this, _initiallyOwnedObjects).add((0, import_utils.normalizeSuiObjectId)(objectChange.objectId)); | ||
} | ||
}); | ||
this.creatorAddress = result.data[0]?.transaction?.data.sender; | ||
if (__privateGet(this, _hasSui) || __privateGet(this, _ownedObjects).length > 0) { | ||
this.claimed = false; | ||
this.assets = await __privateMethod(this, _listNonContractClaimableAssets, listNonContractClaimableAssets_fn).call(this); | ||
} else if (result.data[0]) { | ||
this.claimed = true; | ||
await __privateMethod(this, _loadClaimedAssets, loadClaimedAssets_fn).call(this); | ||
} | ||
}; | ||
function ownedAfterChange(objectChange, address) { | ||
if (objectChange.type === "transferred" && isOwner(objectChange.recipient, address)) { | ||
return true; | ||
} | ||
if ((objectChange.type === "created" || objectChange.type === "mutated") && isOwner(objectChange.owner, address)) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
function isOwner(owner, address) { | ||
return owner && typeof owner === "object" && "AddressOwner" in owner && (0, import_utils.normalizeSuiAddress)(owner.AddressOwner) === address; | ||
} | ||
//# sourceMappingURL=claim.js.map |
@@ -1,21 +0,34 @@ | ||
import '@mysten/sui.js/graphql/schemas/2024-01'; | ||
import type { SuiClient } from '@mysten/sui.js/client'; | ||
import type { ObjectOwner, SuiObjectChange, SuiTransactionBlockResponse } from '@mysten/sui.js/client'; | ||
import type { TransactionBlock } from '@mysten/sui.js/transactions'; | ||
import { ZkSendLink } from './claim.js'; | ||
import type { ZkBagContractOptions } from './zk-bag.js'; | ||
export declare function listCreatedLinks({ address, cursor, network, contract, ...linkOptions }: { | ||
address: string; | ||
contract: ZkBagContractOptions; | ||
cursor?: string; | ||
network?: 'mainnet' | 'testnet'; | ||
host?: string; | ||
path?: string; | ||
client?: SuiClient; | ||
}): Promise<{ | ||
cursor: string | null; | ||
hasNextPage: boolean; | ||
links: ZkSendLink[]; | ||
}>; | ||
export interface LinkAssets { | ||
balances: { | ||
coinType: string; | ||
amount: bigint; | ||
}[]; | ||
nfts: { | ||
objectId: string; | ||
type: string; | ||
version: string; | ||
digest: string; | ||
}[]; | ||
coins: { | ||
objectId: string; | ||
type: string; | ||
version: string; | ||
digest: string; | ||
}[]; | ||
} | ||
export declare function isClaimTransaction(txb: TransactionBlock, options: { | ||
packageId: string; | ||
}): boolean; | ||
export declare function getAssetsFromTxnBlock({ transactionBlock, address, isSent, }: { | ||
transactionBlock: SuiTransactionBlockResponse; | ||
address: string; | ||
isSent: boolean; | ||
}): LinkAssets; | ||
export declare function ownedAfterChange(objectChange: SuiObjectChange, address: string): objectChange is Extract<SuiObjectChange, { | ||
type: 'created' | 'transferred' | 'mutated'; | ||
}>; | ||
export declare function isOwner(owner: ObjectOwner, address: string): owner is { | ||
AddressOwner: string; | ||
}; |
@@ -21,115 +21,9 @@ "use strict"; | ||
__export(utils_exports, { | ||
getAssetsFromTxnBlock: () => getAssetsFromTxnBlock, | ||
isClaimTransaction: () => isClaimTransaction, | ||
listCreatedLinks: () => listCreatedLinks | ||
isOwner: () => isOwner, | ||
ownedAfterChange: () => ownedAfterChange | ||
}); | ||
module.exports = __toCommonJS(utils_exports); | ||
var import__ = require("@mysten/sui.js/graphql/schemas/2024-01"); | ||
var import_bcs = require("@mysten/sui.js/bcs"); | ||
var import_graphql = require("@mysten/sui.js/graphql"); | ||
var import__2 = require("@mysten/sui.js/graphql/schemas/2024-01"); | ||
var import_utils = require("@mysten/sui.js/utils"); | ||
var import_claim = require("./claim.js"); | ||
const ListCreatedLinksQuery = (0, import__2.graphql)(` | ||
query listCreatedLinks($address: SuiAddress!, $function: String!, $cursor: String) { | ||
transactionBlocks( | ||
last: 10 | ||
before: $cursor | ||
filter: { signAddress: $address, function: $function, kind: PROGRAMMABLE_TX } | ||
) { | ||
pageInfo { | ||
startCursor | ||
hasPreviousPage | ||
} | ||
nodes { | ||
digest | ||
kind { | ||
__typename | ||
... on ProgrammableTransactionBlock { | ||
inputs(first: 10) { | ||
nodes { | ||
__typename | ||
... on Pure { | ||
bytes | ||
} | ||
} | ||
} | ||
transactions(first: 10) { | ||
nodes { | ||
__typename | ||
... on MoveCallTransaction { | ||
module | ||
functionName | ||
package | ||
arguments { | ||
__typename | ||
... on Input { | ||
ix | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
`); | ||
async function listCreatedLinks({ | ||
address, | ||
cursor, | ||
network, | ||
contract, | ||
...linkOptions | ||
}) { | ||
const gqlClient = new import_graphql.SuiGraphQLClient({ | ||
url: network === "testnet" ? "https://sui-testnet.mystenlabs.com/graphql" : "https://sui-mainnet.mystenlabs.com/graphql" | ||
}); | ||
const packageId = (0, import_utils.normalizeSuiAddress)(contract.packageId); | ||
const page = await gqlClient.query({ | ||
query: ListCreatedLinksQuery, | ||
variables: { | ||
address, | ||
cursor, | ||
function: `${packageId}::zk_bag::new` | ||
} | ||
}); | ||
const transactionBlocks = page.data?.transactionBlocks; | ||
if (!transactionBlocks || page.errors?.length) { | ||
throw new Error("Failed to load created links"); | ||
} | ||
const links = transactionBlocks.nodes.map((node) => { | ||
if (node.kind?.__typename !== "ProgrammableTransactionBlock") { | ||
throw new Error("Invalid transaction block"); | ||
} | ||
const fn = node.kind.transactions.nodes.find( | ||
(fn2) => fn2.__typename === "MoveCallTransaction" && fn2.package === packageId && fn2.module === "zk_bag" && fn2.functionName === "new" | ||
); | ||
if (fn?.__typename !== "MoveCallTransaction") { | ||
return null; | ||
} | ||
const addressArg = fn.arguments[1]; | ||
if (addressArg.__typename !== "Input") { | ||
throw new Error("Invalid address argument"); | ||
} | ||
const input = node.kind.inputs.nodes[addressArg.ix]; | ||
if (input.__typename !== "Pure") { | ||
throw new Error("Expected Address input to be a Pure value"); | ||
} | ||
const address2 = import_bcs.bcs.Address.parse((0, import_utils.fromB64)(input.bytes)); | ||
return new import_claim.ZkSendLink({ | ||
network, | ||
address: address2, | ||
contract, | ||
isContractLink: true, | ||
...linkOptions | ||
}); | ||
}).reverse().filter(Boolean); | ||
await Promise.all(links.map((link) => link.loadOwnedData())); | ||
return { | ||
cursor: transactionBlocks.pageInfo.startCursor, | ||
hasNextPage: transactionBlocks.pageInfo.hasPreviousPage, | ||
links | ||
}; | ||
} | ||
function isClaimTransaction(txb, options) { | ||
@@ -163,2 +57,65 @@ let transfers = 0; | ||
} | ||
function getAssetsFromTxnBlock({ | ||
transactionBlock, | ||
address, | ||
isSent | ||
}) { | ||
const normalizedAddress = (0, import_utils.normalizeSuiAddress)(address); | ||
const balances = []; | ||
const nfts = []; | ||
const coins = []; | ||
transactionBlock.balanceChanges?.forEach((change) => { | ||
const validAmountChange = isSent ? BigInt(change.amount) < 0n : BigInt(change.amount) > 0n; | ||
if (validAmountChange && isOwner(change.owner, normalizedAddress)) { | ||
balances.push({ | ||
coinType: (0, import_utils.normalizeStructTag)(change.coinType), | ||
amount: BigInt(change.amount) | ||
}); | ||
} | ||
}); | ||
transactionBlock.objectChanges?.forEach((change) => { | ||
if ("objectType" in change) { | ||
const type = (0, import_utils.parseStructTag)(change.objectType); | ||
if (type.address === (0, import_utils.normalizeSuiAddress)("0x2") && type.module === "coin" && type.name === "Coin") { | ||
if (change.type === "created" || change.type === "transferred" || change.type === "mutated") { | ||
coins.push(change); | ||
} | ||
return; | ||
} | ||
} | ||
if (isObjectOwner(change, normalizedAddress, isSent) && (change.type === "created" || change.type === "transferred" || change.type === "mutated")) { | ||
nfts.push(change); | ||
} | ||
}); | ||
return { | ||
balances, | ||
nfts, | ||
coins | ||
}; | ||
} | ||
function getObjectOwnerFromObjectChange(objectChange, isSent) { | ||
if (isSent) { | ||
return "owner" in objectChange ? objectChange.owner : null; | ||
} | ||
return "recipient" in objectChange ? objectChange.recipient : null; | ||
} | ||
function isObjectOwner(objectChange, address, isSent) { | ||
const owner = getObjectOwnerFromObjectChange(objectChange, isSent); | ||
if (isSent) { | ||
return owner && typeof owner === "object" && "AddressOwner" in owner; | ||
} | ||
return ownedAfterChange(objectChange, address); | ||
} | ||
function ownedAfterChange(objectChange, address) { | ||
if (objectChange.type === "transferred" && isOwner(objectChange.recipient, address)) { | ||
return true; | ||
} | ||
if ((objectChange.type === "created" || objectChange.type === "mutated") && isOwner(objectChange.owner, address)) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
function isOwner(owner, address) { | ||
return owner && typeof owner === "object" && "AddressOwner" in owner && (0, import_utils.normalizeSuiAddress)(owner.AddressOwner) === address; | ||
} | ||
//# sourceMappingURL=utils.js.map |
@@ -7,2 +7,3 @@ import type { TransactionArgument, TransactionBlock, TransactionObjectArgument } from '@mysten/sui.js/transactions'; | ||
} | ||
export declare const MAINNET_CONTRACT_IDS: ZkBagContractOptions; | ||
export declare class ZkBag<IDs> { | ||
@@ -9,0 +10,0 @@ #private; |
@@ -39,2 +39,3 @@ "use strict"; | ||
__export(zk_bag_exports, { | ||
MAINNET_CONTRACT_IDS: () => MAINNET_CONTRACT_IDS, | ||
ZkBag: () => ZkBag | ||
@@ -44,2 +45,7 @@ }); | ||
var _package, _module; | ||
const MAINNET_CONTRACT_IDS = { | ||
packageId: "0x5bb7d0bb3240011336ca9015f553b2646302a4f05f821160344e9ec5a988f740", | ||
bagStoreId: "0x65b215a3f2a951c94313a89c43f0adbd2fd9ea78a0badf81e27d1c9868a8b6fe", | ||
bagStoreTableId: "0x616db54ca564660cd58e36a4548be68b289371ef2611485c62c374a60960084e" | ||
}; | ||
class ZkBag { | ||
@@ -46,0 +52,0 @@ constructor(packageAddress, ids) { |
@@ -20,4 +20,4 @@ import type { Output } from 'valibot'; | ||
address: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>], { | ||
@@ -31,4 +31,4 @@ type: "connect"; | ||
address: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>; | ||
@@ -58,4 +58,4 @@ export type ZkSendRequestData = Output<typeof ZkSendRequestData>; | ||
address: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>], { | ||
@@ -69,4 +69,4 @@ type: "connect"; | ||
address: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>; | ||
@@ -84,4 +84,4 @@ }, undefined, { | ||
address: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}; | ||
@@ -103,4 +103,4 @@ name?: string | undefined; | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
}>, import("valibot").ObjectSchema<{ | ||
@@ -112,4 +112,4 @@ type: import("valibot").LiteralSchema<"sign-personal-message", "sign-personal-message">; | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>], { | ||
@@ -120,8 +120,8 @@ address: string; | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
} | { | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>; | ||
@@ -147,4 +147,4 @@ export type ZkSendResponseData = Output<typeof ZkSendResponseData>; | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
}>, import("valibot").ObjectSchema<{ | ||
@@ -156,4 +156,4 @@ type: import("valibot").LiteralSchema<"sign-personal-message", "sign-personal-message">; | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>], { | ||
@@ -164,8 +164,8 @@ address: string; | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
} | { | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>; | ||
@@ -178,8 +178,8 @@ }, undefined, { | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
} | { | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}; | ||
@@ -195,8 +195,8 @@ type: "resolve"; | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
} | { | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}; | ||
@@ -227,4 +227,4 @@ type: "resolve"; | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
}>, import("valibot").ObjectSchema<{ | ||
@@ -236,4 +236,4 @@ type: import("valibot").LiteralSchema<"sign-personal-message", "sign-personal-message">; | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>], { | ||
@@ -244,8 +244,8 @@ address: string; | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
} | { | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}>; | ||
@@ -258,8 +258,8 @@ }, undefined, { | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
} | { | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}; | ||
@@ -275,8 +275,8 @@ type: "resolve"; | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
} | { | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}; | ||
@@ -295,8 +295,8 @@ type: "resolve"; | ||
signature: string; | ||
type: "sign-transaction-block"; | ||
bytes: string; | ||
type: "sign-transaction-block"; | ||
} | { | ||
signature: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}; | ||
@@ -303,0 +303,0 @@ type: "resolve"; |
@@ -31,4 +31,4 @@ import type { Output } from 'valibot'; | ||
address: string; | ||
type: "sign-personal-message"; | ||
bytes: string; | ||
type: "sign-personal-message"; | ||
}; | ||
@@ -35,0 +35,0 @@ name?: string | undefined; |
export { ZkSendLinkBuilder, type ZkSendLinkBuilderOptions, type CreateZkSendLinkOptions, } from './links/builder.js'; | ||
export { ZkSendLink, type ZkSendLinkOptions } from './links/claim.js'; | ||
export { type ZkBagContractOptions } from './links/zk-bag.js'; | ||
export { listCreatedLinks, isClaimTransaction } from './links/utils.js'; | ||
export { type ZkBagContractOptions, ZkBag } from './links/zk-bag.js'; | ||
export { isClaimTransaction } from './links/utils.js'; | ||
export { listCreatedLinks } from './links/list-created-links.js'; | ||
export { MAINNET_CONTRACT_IDS } from './links/zk-bag.js'; | ||
export * from './wallet.js'; | ||
export * from './channel/index.js'; |
@@ -5,7 +5,11 @@ import { | ||
import { ZkSendLink } from "./links/claim.js"; | ||
import {} from "./links/zk-bag.js"; | ||
import { listCreatedLinks, isClaimTransaction } from "./links/utils.js"; | ||
import { ZkBag } from "./links/zk-bag.js"; | ||
import { isClaimTransaction } from "./links/utils.js"; | ||
import { listCreatedLinks } from "./links/list-created-links.js"; | ||
import { MAINNET_CONTRACT_IDS } from "./links/zk-bag.js"; | ||
export * from "./wallet.js"; | ||
export * from "./channel/index.js"; | ||
export { | ||
MAINNET_CONTRACT_IDS, | ||
ZkBag, | ||
ZkSendLink, | ||
@@ -12,0 +16,0 @@ ZkSendLinkBuilder, |
@@ -18,3 +18,3 @@ import { SuiClient } from '@mysten/sui.js/client'; | ||
redirect?: ZkSendLinkRedirect; | ||
contract?: ZkBagContractOptions; | ||
contract?: ZkBagContractOptions | null; | ||
} | ||
@@ -31,2 +31,6 @@ export interface CreateZkSendLinkOptions { | ||
#private; | ||
objectIds: Set<string>; | ||
balances: Map<string, bigint>; | ||
sender: string; | ||
keypair: Keypair; | ||
constructor({ host, path, keypair, network, client, sender, redirect, contract, }: ZkSendLinkBuilderOptions); | ||
@@ -41,4 +45,11 @@ addClaimableMist(amount: bigint): void; | ||
}): Promise<import("@mysten/sui.js/client").SuiTransactionBlockResponse>; | ||
createSendTransaction({ transactionBlock: txb, calculateGas, }?: CreateZkSendLinkOptions): Promise<TransactionBlock>; | ||
createSendTransaction({ transactionBlock, calculateGas, }?: CreateZkSendLinkOptions): Promise<TransactionBlock>; | ||
static createLinks({ links, network, client, transactionBlock, contract: contractIds, }: { | ||
transactionBlock?: TransactionBlock; | ||
client?: SuiClient; | ||
network?: 'mainnet' | 'testnet'; | ||
links: ZkSendLinkBuilder[]; | ||
contract: ZkBagContractOptions; | ||
}): Promise<TransactionBlock>; | ||
} | ||
export {}; |
@@ -23,3 +23,3 @@ var __accessCheck = (obj, member, msg) => { | ||
}; | ||
var _host, _path, _keypair, _client, _redirect, _objects, _balances, _sender, _coinsByType, _contract, _objectsToTransfer, objectsToTransfer_fn, _createSendTransactionWithoutContract, createSendTransactionWithoutContract_fn, _estimateClaimGasFee, estimateClaimGasFee_fn, _getCoinsByType, getCoinsByType_fn; | ||
var _host, _path, _client, _redirect, _coinsByType, _contract, _objectsToTransfer, objectsToTransfer_fn, _createSendTransactionWithoutContract, createSendTransactionWithoutContract_fn, _estimateClaimGasFee, estimateClaimGasFee_fn, _getCoinsByType, getCoinsByType_fn; | ||
import { getFullnodeUrl, SuiClient } from "@mysten/sui.js/client"; | ||
@@ -30,3 +30,3 @@ import { decodeSuiPrivateKey } from "@mysten/sui.js/cryptography"; | ||
import { normalizeStructTag, normalizeSuiAddress, SUI_TYPE_ARG, toB64 } from "@mysten/sui.js/utils"; | ||
import { ZkBag } from "./zk-bag.js"; | ||
import { MAINNET_CONTRACT_IDS, ZkBag } from "./zk-bag.js"; | ||
const DEFAULT_ZK_SEND_LINK_OPTIONS = { | ||
@@ -38,3 +38,3 @@ host: "https://zksend.com", | ||
const SUI_COIN_TYPE = normalizeStructTag(SUI_TYPE_ARG); | ||
class ZkSendLinkBuilder { | ||
const _ZkSendLinkBuilder = class { | ||
constructor({ | ||
@@ -48,3 +48,3 @@ host = DEFAULT_ZK_SEND_LINK_OPTIONS.host, | ||
redirect, | ||
contract | ||
contract = network === "mainnet" ? MAINNET_CONTRACT_IDS : void 0 | ||
}) { | ||
@@ -55,10 +55,8 @@ __privateAdd(this, _objectsToTransfer); | ||
__privateAdd(this, _getCoinsByType); | ||
this.objectIds = /* @__PURE__ */ new Set(); | ||
this.balances = /* @__PURE__ */ new Map(); | ||
__privateAdd(this, _host, void 0); | ||
__privateAdd(this, _path, void 0); | ||
__privateAdd(this, _keypair, void 0); | ||
__privateAdd(this, _client, void 0); | ||
__privateAdd(this, _redirect, void 0); | ||
__privateAdd(this, _objects, /* @__PURE__ */ new Set()); | ||
__privateAdd(this, _balances, /* @__PURE__ */ new Map()); | ||
__privateAdd(this, _sender, void 0); | ||
__privateAdd(this, _coinsByType, /* @__PURE__ */ new Map()); | ||
@@ -69,5 +67,5 @@ __privateAdd(this, _contract, void 0); | ||
__privateSet(this, _redirect, redirect); | ||
__privateSet(this, _keypair, keypair); | ||
this.keypair = keypair; | ||
__privateSet(this, _client, client); | ||
__privateSet(this, _sender, normalizeSuiAddress(sender)); | ||
this.sender = normalizeSuiAddress(sender); | ||
if (contract) { | ||
@@ -82,6 +80,6 @@ __privateSet(this, _contract, new ZkBag(contract.packageId, contract)); | ||
const normalizedType = normalizeStructTag(coinType); | ||
__privateGet(this, _balances).set(normalizedType, (__privateGet(this, _balances).get(normalizedType) ?? 0n) + amount); | ||
this.balances.set(normalizedType, (this.balances.get(normalizedType) ?? 0n) + amount); | ||
} | ||
addClaimableObject(id) { | ||
__privateGet(this, _objects).add(id); | ||
this.objectIds.add(id); | ||
} | ||
@@ -92,3 +90,3 @@ getLink() { | ||
link.hash = `${__privateGet(this, _contract) ? "$" : ""}${toB64( | ||
decodeSuiPrivateKey(__privateGet(this, _keypair).getSecretKey()).secretKey | ||
decodeSuiPrivateKey(this.keypair.getSecretKey()).secretKey | ||
)}`; | ||
@@ -118,30 +116,120 @@ if (__privateGet(this, _redirect)) { | ||
async createSendTransaction({ | ||
transactionBlock: txb = new TransactionBlock(), | ||
transactionBlock = new TransactionBlock(), | ||
calculateGas | ||
} = {}) { | ||
if (!__privateGet(this, _contract)) { | ||
return __privateMethod(this, _createSendTransactionWithoutContract, createSendTransactionWithoutContract_fn).call(this, { transactionBlock: txb, calculateGas }); | ||
return __privateMethod(this, _createSendTransactionWithoutContract, createSendTransactionWithoutContract_fn).call(this, { transactionBlock, calculateGas }); | ||
} | ||
const receiver = txb.pure.address(__privateGet(this, _keypair).toSuiAddress()); | ||
const store = txb.object(__privateGet(this, _contract).ids.bagStoreId); | ||
__privateGet(this, _contract).new(txb, { arguments: [store, receiver] }); | ||
txb.setSenderIfNotSet(__privateGet(this, _sender)); | ||
const objectsToTransfer = await __privateMethod(this, _objectsToTransfer, objectsToTransfer_fn).call(this, txb); | ||
for (const object of objectsToTransfer) { | ||
__privateGet(this, _contract).add(txb, { | ||
arguments: [store, receiver, object.ref], | ||
typeArguments: [object.type] | ||
transactionBlock.setSenderIfNotSet(this.sender); | ||
return _ZkSendLinkBuilder.createLinks({ | ||
transactionBlock, | ||
client: __privateGet(this, _client), | ||
contract: __privateGet(this, _contract).ids, | ||
links: [this] | ||
}); | ||
} | ||
static async createLinks({ | ||
links, | ||
network = "mainnet", | ||
client = new SuiClient({ url: getFullnodeUrl(network) }), | ||
transactionBlock = new TransactionBlock(), | ||
contract: contractIds = MAINNET_CONTRACT_IDS | ||
}) { | ||
const contract = new ZkBag(contractIds.packageId, contractIds); | ||
const store = transactionBlock.object(contract.ids.bagStoreId); | ||
const coinsByType = /* @__PURE__ */ new Map(); | ||
const allIds = links.flatMap((link) => [...link.objectIds]); | ||
await Promise.all( | ||
[...new Set(links.flatMap((link) => [...link.balances.keys()]))].map(async (coinType) => { | ||
const coins = await client.getCoins({ | ||
coinType, | ||
owner: links[0].sender | ||
}); | ||
coinsByType.set( | ||
coinType, | ||
coins.data.filter((coin) => !allIds.includes(coin.coinObjectId)) | ||
); | ||
}) | ||
); | ||
const objectRefs = /* @__PURE__ */ new Map(); | ||
const pageSize = 50; | ||
let offset = 0; | ||
while (offset < allIds.length) { | ||
let chunk = allIds.slice(offset, offset + pageSize); | ||
offset += pageSize; | ||
const objects = await client.multiGetObjects({ | ||
ids: chunk, | ||
options: { | ||
showType: true | ||
} | ||
}); | ||
for (const [i, res] of objects.entries()) { | ||
if (!res.data || res.error) { | ||
throw new Error(`Failed to load object ${chunk[i]} (${res.error?.code})`); | ||
} | ||
objectRefs.set(chunk[i], { | ||
ref: transactionBlock.objectRef({ | ||
version: res.data.version, | ||
digest: res.data.digest, | ||
objectId: res.data.objectId | ||
}), | ||
type: res.data.type | ||
}); | ||
} | ||
} | ||
return txb; | ||
const mergedCoins = /* @__PURE__ */ new Map([ | ||
[SUI_COIN_TYPE, transactionBlock.gas] | ||
]); | ||
for (const [coinType, coins] of coinsByType) { | ||
if (coinType === SUI_COIN_TYPE) { | ||
continue; | ||
} | ||
const [first, ...rest] = coins.map( | ||
(coin) => transactionBlock.objectRef({ | ||
objectId: coin.coinObjectId, | ||
version: coin.version, | ||
digest: coin.digest | ||
}) | ||
); | ||
if (rest.length > 0) { | ||
transactionBlock.mergeCoins(first, rest); | ||
} | ||
mergedCoins.set(coinType, transactionBlock.object(first)); | ||
} | ||
for (const link of links) { | ||
const receiver = link.keypair.toSuiAddress(); | ||
contract.new(transactionBlock, { arguments: [store, receiver] }); | ||
link.objectIds.forEach((id) => { | ||
const object = objectRefs.get(id); | ||
if (!object) { | ||
throw new Error(`Object ${id} not found`); | ||
} | ||
contract.add(transactionBlock, { | ||
arguments: [store, receiver, object.ref], | ||
typeArguments: [object.type] | ||
}); | ||
}); | ||
} | ||
for (const [coinType, merged] of mergedCoins) { | ||
const linksWithCoin = links.filter((link) => link.balances.has(coinType)); | ||
if (linksWithCoin.length === 0) { | ||
continue; | ||
} | ||
const balances = linksWithCoin.map((link) => link.balances.get(coinType)); | ||
const splits = transactionBlock.splitCoins(merged, balances); | ||
for (const [i, link] of linksWithCoin.entries()) { | ||
contract.add(transactionBlock, { | ||
arguments: [store, link.keypair.toSuiAddress(), splits[i]], | ||
typeArguments: [`0x2::coin::Coin<${coinType}>`] | ||
}); | ||
} | ||
} | ||
return transactionBlock; | ||
} | ||
} | ||
}; | ||
let ZkSendLinkBuilder = _ZkSendLinkBuilder; | ||
_host = new WeakMap(); | ||
_path = new WeakMap(); | ||
_keypair = new WeakMap(); | ||
_client = new WeakMap(); | ||
_redirect = new WeakMap(); | ||
_objects = new WeakMap(); | ||
_balances = new WeakMap(); | ||
_sender = new WeakMap(); | ||
_coinsByType = new WeakMap(); | ||
@@ -151,3 +239,3 @@ _contract = new WeakMap(); | ||
objectsToTransfer_fn = async function(txb) { | ||
const objectIDs = [...__privateGet(this, _objects)]; | ||
const objectIDs = [...this.objectIds]; | ||
const refsWithType = (await __privateGet(this, _client).multiGetObjects({ | ||
@@ -171,4 +259,4 @@ ids: objectIDs, | ||
}); | ||
txb.setSenderIfNotSet(__privateGet(this, _sender)); | ||
for (const [coinType, amount] of __privateGet(this, _balances)) { | ||
txb.setSenderIfNotSet(this.sender); | ||
for (const [coinType, amount] of this.balances) { | ||
if (coinType === SUI_COIN_TYPE) { | ||
@@ -201,4 +289,4 @@ const [sui] = txb.splitCoins(txb.gas, [amount]); | ||
const baseGasAmount = calculateGas ? await calculateGas({ | ||
balances: __privateGet(this, _balances), | ||
objects: [...__privateGet(this, _objects)], | ||
balances: this.balances, | ||
objects: [...this.objectIds], | ||
gasEstimateFromDryRun | ||
@@ -208,7 +296,7 @@ }) : gasEstimateFromDryRun * 2n; | ||
const roundedGasAmount = gasWithBuffer - gasWithBuffer % 1000n - 13n; | ||
const address = __privateGet(this, _keypair).toSuiAddress(); | ||
const address = this.keypair.toSuiAddress(); | ||
const objectsToTransfer = (await __privateMethod(this, _objectsToTransfer, objectsToTransfer_fn).call(this, txb)).map((obj) => obj.ref); | ||
const [gas] = txb.splitCoins(txb.gas, [roundedGasAmount]); | ||
objectsToTransfer.push(gas); | ||
txb.setSenderIfNotSet(__privateGet(this, _sender)); | ||
txb.setSenderIfNotSet(this.sender); | ||
txb.transferObjects(objectsToTransfer, address); | ||
@@ -220,7 +308,7 @@ return txb; | ||
const txb = new TransactionBlock(); | ||
txb.setSender(__privateGet(this, _sender)); | ||
txb.setSender(this.sender); | ||
txb.setGasPayment([]); | ||
txb.transferObjects([txb.gas], __privateGet(this, _keypair).toSuiAddress()); | ||
const idsToTransfer = [...__privateGet(this, _objects)]; | ||
for (const [coinType] of __privateGet(this, _balances)) { | ||
txb.transferObjects([txb.gas], this.keypair.toSuiAddress()); | ||
const idsToTransfer = [...this.objectIds]; | ||
for (const [coinType] of this.balances) { | ||
const coins = await __privateMethod(this, _getCoinsByType, getCoinsByType_fn).call(this, coinType); | ||
@@ -235,3 +323,3 @@ if (!coins.length) { | ||
idsToTransfer.map((id) => txb.object(id)), | ||
__privateGet(this, _keypair).toSuiAddress() | ||
this.keypair.toSuiAddress() | ||
); | ||
@@ -251,3 +339,3 @@ } | ||
coinType, | ||
owner: __privateGet(this, _sender) | ||
owner: this.sender | ||
}); | ||
@@ -254,0 +342,0 @@ __privateGet(this, _coinsByType).set(coinType, coins.data); |
@@ -5,2 +5,3 @@ import { SuiClient } from '@mysten/sui.js/client'; | ||
import type { ZkSendLinkBuilderOptions } from './builder.js'; | ||
import type { LinkAssets } from './utils.js'; | ||
import type { ZkBagContractOptions } from './zk-bag.js'; | ||
@@ -15,2 +16,4 @@ export type ZkSendLinkOptions = { | ||
address?: string; | ||
isContractLink: boolean; | ||
contract?: ZkBagContractOptions | null; | ||
} & ({ | ||
@@ -22,8 +25,2 @@ address: string; | ||
address?: never; | ||
}) & ({ | ||
isContractLink: true; | ||
contract: ZkBagContractOptions; | ||
} | { | ||
isContractLink: false; | ||
contract?: never; | ||
}); | ||
@@ -35,44 +32,10 @@ export declare class ZkSendLink { | ||
creatorAddress?: string; | ||
assets?: LinkAssets; | ||
claimed?: boolean; | ||
constructor({ network, claimApi, client, keypair, contract, address, host, path, isContractLink, }: ZkSendLinkOptions); | ||
static fromUrl(url: string, { contract, ...options }?: Omit<ZkSendLinkOptions, 'keypair' | 'address' | 'isContractLink'>): Promise<ZkSendLink>; | ||
static fromAddress(address: string, options: Omit<ZkSendLinkOptions, 'keypair' | 'address' | 'isContractLink'> & { | ||
contract: ZkBagContractOptions; | ||
}): Promise<ZkSendLink>; | ||
loadOwnedData(): Promise<void>; | ||
listClaimableAssets(address: string, options?: { | ||
claimObjectsAddedAfterCreation?: boolean; | ||
coinTypes?: string[]; | ||
objects?: string[]; | ||
}): Promise<{ | ||
balances: { | ||
coinType: string; | ||
amount: bigint; | ||
}[]; | ||
nfts: { | ||
objectId: string; | ||
type: string; | ||
version: string; | ||
digest: string; | ||
}[]; | ||
coins: { | ||
objectId: string; | ||
type: string; | ||
version: string; | ||
digest: string; | ||
}[]; | ||
}>; | ||
claimAssets(address: string, | ||
/** @deprecated filtering claims is not supported in contract based links */ | ||
options?: { | ||
claimObjectsAddedAfterCreation?: boolean; | ||
coinTypes?: string[]; | ||
objects?: string[]; | ||
}): Promise<import("@mysten/sui.js/client").SuiTransactionBlockResponse>; | ||
createClaimTransaction(address: string, { reclaim, ...options }?: { | ||
/** @deprecated filtering claims is not supported in contract based links */ | ||
claimObjectsAddedAfterCreation?: boolean; | ||
/** @deprecated filtering claims is not supported in contract based links */ | ||
coinTypes?: string[]; | ||
/** @deprecated filtering claims is not supported in contract based links */ | ||
objects?: string[]; | ||
static fromUrl(url: string, options?: Omit<ZkSendLinkOptions, 'keypair' | 'address' | 'isContractLink'>): Promise<ZkSendLink>; | ||
static fromAddress(address: string, options: Omit<ZkSendLinkOptions, 'keypair' | 'address' | 'isContractLink'>): Promise<ZkSendLink>; | ||
loadAssets(): Promise<void>; | ||
claimAssets(address: string): Promise<import("@mysten/sui.js/client").SuiTransactionBlockResponse>; | ||
createClaimTransaction(address: string, { reclaim, }?: { | ||
reclaim?: boolean; | ||
@@ -79,0 +42,0 @@ }): TransactionBlock; |
@@ -23,3 +23,4 @@ var __accessCheck = (obj, member, msg) => { | ||
}; | ||
var _client, _initiallyOwnedObjects, _ownedObjects, _bagObjects, _gasCoin, _hasSui, _contract, _claimApi, _network, _host, _path, _loadBag, loadBag_fn, _createSponsoredTransactionBlock, createSponsoredTransactionBlock_fn, _executeSponsoredTransactionBlock, executeSponsoredTransactionBlock_fn, _fetch, fetch_fn, _listNonContractClaimableAssets, listNonContractClaimableAssets_fn, _createNonContractClaimTransaction, createNonContractClaimTransaction_fn, _loadOwnedObjects, loadOwnedObjects_fn, _loadInitialTransactionData, loadInitialTransactionData_fn; | ||
var _client, _contract, _claimApi, _network, _host, _path, _gasCoin, _hasSui, _ownedObjects, _loadBag, loadBag_fn, _loadClaimedAssets, loadClaimedAssets_fn, _createSponsoredTransactionBlock, createSponsoredTransactionBlock_fn, _executeSponsoredTransactionBlock, executeSponsoredTransactionBlock_fn, _fetch, fetch_fn, _listNonContractClaimableAssets, listNonContractClaimableAssets_fn, _createNonContractClaimTransaction, createNonContractClaimTransaction_fn, _loadOwnedObjects, loadOwnedObjects_fn; | ||
import { bcs } from "@mysten/sui.js/bcs"; | ||
import { getFullnodeUrl, SuiClient } from "@mysten/sui.js/client"; | ||
@@ -38,3 +39,4 @@ import { Ed25519Keypair } from "@mysten/sui.js/keypairs/ed25519"; | ||
import { ZkSendLinkBuilder } from "./builder.js"; | ||
import { ZkBag } from "./zk-bag.js"; | ||
import { getAssetsFromTxnBlock, isOwner, ownedAfterChange } from "./utils.js"; | ||
import { MAINNET_CONTRACT_IDS, ZkBag } from "./zk-bag.js"; | ||
const DEFAULT_ZK_SEND_LINK_OPTIONS = { | ||
@@ -54,3 +56,3 @@ host: "https://zksend.com", | ||
keypair, | ||
contract, | ||
contract = network === "mainnet" ? MAINNET_CONTRACT_IDS : null, | ||
address, | ||
@@ -62,2 +64,3 @@ host, | ||
__privateAdd(this, _loadBag); | ||
__privateAdd(this, _loadClaimedAssets); | ||
__privateAdd(this, _createSponsoredTransactionBlock); | ||
@@ -69,9 +72,3 @@ __privateAdd(this, _executeSponsoredTransactionBlock); | ||
__privateAdd(this, _loadOwnedObjects); | ||
__privateAdd(this, _loadInitialTransactionData); | ||
__privateAdd(this, _client, void 0); | ||
__privateAdd(this, _initiallyOwnedObjects, /* @__PURE__ */ new Set()); | ||
__privateAdd(this, _ownedObjects, []); | ||
__privateAdd(this, _bagObjects, null); | ||
__privateAdd(this, _gasCoin, void 0); | ||
__privateAdd(this, _hasSui, false); | ||
__privateAdd(this, _contract, void 0); | ||
@@ -82,2 +79,6 @@ __privateAdd(this, _claimApi, void 0); | ||
__privateAdd(this, _path, void 0); | ||
// State for non-contract based links | ||
__privateAdd(this, _gasCoin, void 0); | ||
__privateAdd(this, _hasSui, false); | ||
__privateAdd(this, _ownedObjects, []); | ||
if (!keypair && !address) { | ||
@@ -100,6 +101,3 @@ throw new Error("Either keypair or address must be provided"); | ||
} | ||
static async fromUrl(url, { | ||
contract, | ||
...options | ||
} = {}) { | ||
static async fromUrl(url, options = {}) { | ||
const parsed = new URL(url); | ||
@@ -109,5 +107,2 @@ const isContractLink = parsed.hash.startsWith("#$"); | ||
if (isContractLink) { | ||
if (!contract) { | ||
throw new Error("Contract options are required for contract based links"); | ||
} | ||
const keypair = Ed25519Keypair.fromSecretKey(fromB64(parsed.hash.slice(2))); | ||
@@ -119,4 +114,3 @@ link = new _ZkSendLink({ | ||
path: parsed.pathname, | ||
isContractLink: true, | ||
contract | ||
isContractLink: true | ||
}); | ||
@@ -135,3 +129,3 @@ } else { | ||
} | ||
await link.loadOwnedData(); | ||
await link.loadAssets(); | ||
return link; | ||
@@ -145,56 +139,29 @@ } | ||
}); | ||
await link.loadOwnedData(); | ||
await link.loadAssets(); | ||
return link; | ||
} | ||
async loadOwnedData() { | ||
async loadAssets() { | ||
if (__privateGet(this, _contract)) { | ||
await __privateMethod(this, _loadBag, loadBag_fn).call(this); | ||
} else { | ||
await Promise.all([__privateMethod(this, _loadInitialTransactionData, loadInitialTransactionData_fn).call(this), __privateMethod(this, _loadOwnedObjects, loadOwnedObjects_fn).call(this)]); | ||
await __privateMethod(this, _loadOwnedObjects, loadOwnedObjects_fn).call(this); | ||
} | ||
} | ||
async listClaimableAssets(address, options) { | ||
if (!__privateGet(this, _contract)) { | ||
return __privateMethod(this, _listNonContractClaimableAssets, listNonContractClaimableAssets_fn).call(this, address, options); | ||
} | ||
const coins = []; | ||
const nfts = []; | ||
for (const object of __privateGet(this, _bagObjects) ?? []) { | ||
const type = parseStructTag(object.type); | ||
if (type.address === normalizeSuiAddress("0x2") && type.module === "coin" && type.name === "Coin") { | ||
coins.push(object); | ||
} else { | ||
nfts.push(object); | ||
} | ||
} | ||
const balances = /* @__PURE__ */ new Map(); | ||
coins.forEach((coin) => { | ||
if (coin.content?.dataType !== "moveObject") { | ||
return; | ||
} | ||
const amount = BigInt(coin.content.fields.balance); | ||
const coinType = normalizeStructTag(parseStructTag(coin.content.type).typeParams[0]); | ||
if (!balances.has(coinType)) { | ||
balances.set(coinType, { coinType, amount }); | ||
} else { | ||
balances.get(coinType).amount += amount; | ||
} | ||
}); | ||
return { | ||
balances: [...balances.values()], | ||
nfts, | ||
coins | ||
}; | ||
} | ||
async claimAssets(address, options) { | ||
async claimAssets(address) { | ||
if (!this.keypair) { | ||
throw new Error("Cannot claim assets without links keypair"); | ||
} | ||
const txb = this.createClaimTransaction(address, options); | ||
if (!__privateGet(this, _contract) || !__privateGet(this, _bagObjects)) { | ||
if (this.claimed) { | ||
throw new Error("Assets have already been claimed"); | ||
} | ||
if (!__privateGet(this, _contract)) { | ||
return __privateGet(this, _client).signAndExecuteTransactionBlock({ | ||
transactionBlock: txb, | ||
transactionBlock: this.createClaimTransaction(address), | ||
signer: this.keypair | ||
}); | ||
} | ||
if (!this.assets) { | ||
await __privateMethod(this, _loadBag, loadBag_fn).call(this); | ||
} | ||
const txb = this.createClaimTransaction(address); | ||
const { digest } = await __privateMethod(this, _executeSponsoredTransactionBlock, executeSponsoredTransactionBlock_fn).call(this, await __privateMethod(this, _createSponsoredTransactionBlock, createSponsoredTransactionBlock_fn).call(this, txb, address, this.keypair.toSuiAddress()), this.keypair); | ||
@@ -204,11 +171,7 @@ return __privateGet(this, _client).waitForTransactionBlock({ digest }); | ||
createClaimTransaction(address, { | ||
reclaim, | ||
...options | ||
reclaim | ||
} = {}) { | ||
if (!__privateGet(this, _contract)) { | ||
return __privateMethod(this, _createNonContractClaimTransaction, createNonContractClaimTransaction_fn).call(this, address, options); | ||
return __privateMethod(this, _createNonContractClaimTransaction, createNonContractClaimTransaction_fn).call(this, address); | ||
} | ||
if (Object.keys(options).length > 0) { | ||
throw new Error("Filtering claims is not supported for contract based links"); | ||
} | ||
if (!this.keypair && !reclaim) { | ||
@@ -223,3 +186,4 @@ throw new Error("Cannot claim assets without the links keypair"); | ||
const objectsToTransfer = []; | ||
for (const object of __privateGet(this, _bagObjects) ?? []) { | ||
const objects = [...this.assets?.coins ?? [], ...this.assets?.nfts ?? []]; | ||
for (const object of objects) { | ||
objectsToTransfer.push( | ||
@@ -247,3 +211,9 @@ __privateGet(this, _contract).claim(txb, { | ||
async createRegenerateTransaction(sender, options = {}) { | ||
if (!__privateGet(this, _contract) || !__privateGet(this, _bagObjects)) { | ||
if (!this.assets) { | ||
await __privateMethod(this, _loadBag, loadBag_fn).call(this); | ||
} | ||
if (this.claimed) { | ||
throw new Error("Assets have already been claimed"); | ||
} | ||
if (!__privateGet(this, _contract)) { | ||
throw new Error("Regenerating non-contract based links is not supported"); | ||
@@ -274,7 +244,2 @@ } | ||
_client = new WeakMap(); | ||
_initiallyOwnedObjects = new WeakMap(); | ||
_ownedObjects = new WeakMap(); | ||
_bagObjects = new WeakMap(); | ||
_gasCoin = new WeakMap(); | ||
_hasSui = new WeakMap(); | ||
_contract = new WeakMap(); | ||
@@ -285,2 +250,5 @@ _claimApi = new WeakMap(); | ||
_path = new WeakMap(); | ||
_gasCoin = new WeakMap(); | ||
_hasSui = new WeakMap(); | ||
_ownedObjects = new WeakMap(); | ||
_loadBag = new WeakSet(); | ||
@@ -291,2 +259,7 @@ loadBag_fn = async function() { | ||
} | ||
this.assets = { | ||
balances: [], | ||
nfts: [], | ||
coins: [] | ||
}; | ||
const bagField = await __privateGet(this, _client).getDynamicFieldObject({ | ||
@@ -300,3 +273,7 @@ parentId: __privateGet(this, _contract).ids.bagStoreTableId, | ||
if (!bagField.data) { | ||
this.claimed = true; | ||
await __privateMethod(this, _loadClaimedAssets, loadClaimedAssets_fn).call(this); | ||
return; | ||
} else { | ||
this.claimed = false; | ||
} | ||
@@ -315,15 +292,78 @@ const itemIds = bagField.data?.content?.fields?.value?.fields?.item_ids.fields.contents; | ||
}); | ||
__privateSet(this, _bagObjects, objectsResponse.map((object, i) => { | ||
if (!object.data) { | ||
const balances = /* @__PURE__ */ new Map(); | ||
objectsResponse.forEach((object, i) => { | ||
if (!object.data || !object.data.type) { | ||
throw new Error(`Failed to load claimable object ${itemIds[i]}`); | ||
} | ||
return { | ||
objectId: object.data.objectId, | ||
type: normalizeStructTag(object.data.type), | ||
version: object.data.version, | ||
digest: object.data.digest, | ||
content: object.data.content | ||
}; | ||
})); | ||
const type = parseStructTag(normalizeStructTag(object.data.type)); | ||
if (type.address === normalizeSuiAddress("0x2") && type.module === "coin" && type.name === "Coin") { | ||
this.assets.coins.push({ | ||
objectId: object.data.objectId, | ||
type: object.data.type, | ||
version: object.data.version, | ||
digest: object.data.digest | ||
}); | ||
if (object.data.content?.dataType === "moveObject") { | ||
const amount = BigInt(object.data.content.fields.balance); | ||
const coinType = normalizeStructTag( | ||
parseStructTag(object.data.content.type).typeParams[0] | ||
); | ||
if (!balances.has(coinType)) { | ||
balances.set(coinType, { coinType, amount }); | ||
} else { | ||
balances.get(coinType).amount += amount; | ||
} | ||
} | ||
} else { | ||
this.assets.nfts.push({ | ||
objectId: object.data.objectId, | ||
type: object.data.type, | ||
version: object.data.version, | ||
digest: object.data.digest | ||
}); | ||
} | ||
}); | ||
this.assets.balances = [...balances.values()]; | ||
}; | ||
_loadClaimedAssets = new WeakSet(); | ||
loadClaimedAssets_fn = async function() { | ||
const result = await __privateGet(this, _client).queryTransactionBlocks({ | ||
limit: 1, | ||
filter: { | ||
FromAddress: this.address | ||
}, | ||
options: { | ||
showObjectChanges: true, | ||
showBalanceChanges: true, | ||
showInput: true | ||
} | ||
}); | ||
if (!result?.data[0]) { | ||
return; | ||
} | ||
const [txb] = result.data; | ||
if (txb.transaction?.data.transaction.kind !== "ProgrammableTransaction") { | ||
return; | ||
} | ||
const transfer = txb.transaction.data.transaction.transactions.findLast( | ||
(tx) => "TransferObjects" in tx | ||
); | ||
if (!transfer) { | ||
return; | ||
} | ||
const receiverArg = transfer.TransferObjects[1]; | ||
if (!(typeof receiverArg === "object" && "Input" in receiverArg)) { | ||
return; | ||
} | ||
const input = txb.transaction.data.transaction.inputs[receiverArg.Input]; | ||
if (input.type !== "pure") { | ||
return; | ||
} | ||
const receiver = typeof input.value === "string" ? input.value : bcs.Address.parse(new Uint8Array(input.value.Pure)); | ||
this.assets = getAssetsFromTxnBlock({ | ||
transactionBlock: txb, | ||
address: receiver, | ||
isSent: false | ||
}); | ||
}; | ||
_createSponsoredTransactionBlock = new WeakSet(); | ||
@@ -379,5 +419,16 @@ createSponsoredTransactionBlock_fn = async function(txb, claimer, sender) { | ||
_listNonContractClaimableAssets = new WeakSet(); | ||
listNonContractClaimableAssets_fn = async function(address, options) { | ||
listNonContractClaimableAssets_fn = async function() { | ||
const balances = []; | ||
const nfts = []; | ||
const coins = []; | ||
if (__privateGet(this, _ownedObjects).length === 0 && !__privateGet(this, _hasSui)) { | ||
return { | ||
balances, | ||
nfts, | ||
coins | ||
}; | ||
} | ||
const address = new Ed25519Keypair().toSuiAddress(); | ||
const normalizedAddress = normalizeSuiAddress(address); | ||
const txb = this.createClaimTransaction(normalizedAddress, options); | ||
const txb = this.createClaimTransaction(normalizedAddress); | ||
if (__privateGet(this, _gasCoin) || !__privateGet(this, _hasSui)) { | ||
@@ -389,5 +440,2 @@ txb.setGasPayment([]); | ||
}); | ||
const balances = []; | ||
const nfts = []; | ||
const coins = []; | ||
dryRun.balanceChanges.forEach((balanceChange) => { | ||
@@ -422,12 +470,8 @@ if (BigInt(balanceChange.amount) > 0n && isOwner(balanceChange.owner, normalizedAddress)) { | ||
_createNonContractClaimTransaction = new WeakSet(); | ||
createNonContractClaimTransaction_fn = function(address, options) { | ||
createNonContractClaimTransaction_fn = function(address) { | ||
if (!this.keypair) { | ||
throw new Error("Cannot claim assets without the links keypair"); | ||
} | ||
const claimAll = !options?.coinTypes && !options?.objects; | ||
const txb = new TransactionBlock(); | ||
txb.setSender(this.keypair.toSuiAddress()); | ||
const coinTypes = new Set( | ||
options?.coinTypes?.map((type) => normalizeStructTag(`0x2::coin::Coin<${type}>`)) ?? [] | ||
); | ||
const objectsToTransfer = __privateGet(this, _ownedObjects).filter((object) => { | ||
@@ -441,13 +485,7 @@ if (__privateGet(this, _gasCoin)) { | ||
} | ||
if (coinTypes?.has(object.type) || options?.objects?.includes(object.objectId)) { | ||
return true; | ||
} | ||
if (!options?.claimObjectsAddedAfterCreation && !__privateGet(this, _initiallyOwnedObjects).has(object.objectId)) { | ||
return false; | ||
} | ||
return claimAll; | ||
return true; | ||
}).map((object) => txb.object(object.objectId)); | ||
if (__privateGet(this, _gasCoin) && this.creatorAddress) { | ||
txb.transferObjects([txb.gas], this.creatorAddress); | ||
} else if (claimAll || coinTypes?.has(SUI_COIN_TYPE)) { | ||
} else { | ||
objectsToTransfer.push(txb.gas); | ||
@@ -462,3 +500,7 @@ } | ||
loadOwnedObjects_fn = async function() { | ||
__privateSet(this, _ownedObjects, []); | ||
this.assets = { | ||
nfts: [], | ||
balances: [], | ||
coins: [] | ||
}; | ||
let nextCursor; | ||
@@ -470,3 +512,4 @@ do { | ||
options: { | ||
showType: true | ||
showType: true, | ||
showContent: true | ||
} | ||
@@ -492,5 +535,2 @@ }); | ||
__privateSet(this, _gasCoin, coins.data.find((coin) => BigInt(coin.balance) % 1000n === 987n)); | ||
}; | ||
_loadInitialTransactionData = new WeakSet(); | ||
loadInitialTransactionData_fn = async function() { | ||
const result = await __privateGet(this, _client).queryTransactionBlocks({ | ||
@@ -503,25 +543,16 @@ limit: 1, | ||
options: { | ||
showObjectChanges: true, | ||
showInput: true | ||
showInput: true, | ||
showBalanceChanges: true, | ||
showObjectChanges: true | ||
} | ||
}); | ||
result.data[0]?.objectChanges?.forEach((objectChange) => { | ||
if (ownedAfterChange(objectChange, this.address)) { | ||
__privateGet(this, _initiallyOwnedObjects).add(normalizeSuiObjectId(objectChange.objectId)); | ||
} | ||
}); | ||
this.creatorAddress = result.data[0]?.transaction?.data.sender; | ||
if (__privateGet(this, _hasSui) || __privateGet(this, _ownedObjects).length > 0) { | ||
this.claimed = false; | ||
this.assets = await __privateMethod(this, _listNonContractClaimableAssets, listNonContractClaimableAssets_fn).call(this); | ||
} else if (result.data[0]) { | ||
this.claimed = true; | ||
await __privateMethod(this, _loadClaimedAssets, loadClaimedAssets_fn).call(this); | ||
} | ||
}; | ||
function ownedAfterChange(objectChange, address) { | ||
if (objectChange.type === "transferred" && isOwner(objectChange.recipient, address)) { | ||
return true; | ||
} | ||
if ((objectChange.type === "created" || objectChange.type === "mutated") && isOwner(objectChange.owner, address)) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
function isOwner(owner, address) { | ||
return owner && typeof owner === "object" && "AddressOwner" in owner && normalizeSuiAddress(owner.AddressOwner) === address; | ||
} | ||
export { | ||
@@ -528,0 +559,0 @@ ZkSendLink |
@@ -1,21 +0,34 @@ | ||
import '@mysten/sui.js/graphql/schemas/2024-01'; | ||
import type { SuiClient } from '@mysten/sui.js/client'; | ||
import type { ObjectOwner, SuiObjectChange, SuiTransactionBlockResponse } from '@mysten/sui.js/client'; | ||
import type { TransactionBlock } from '@mysten/sui.js/transactions'; | ||
import { ZkSendLink } from './claim.js'; | ||
import type { ZkBagContractOptions } from './zk-bag.js'; | ||
export declare function listCreatedLinks({ address, cursor, network, contract, ...linkOptions }: { | ||
address: string; | ||
contract: ZkBagContractOptions; | ||
cursor?: string; | ||
network?: 'mainnet' | 'testnet'; | ||
host?: string; | ||
path?: string; | ||
client?: SuiClient; | ||
}): Promise<{ | ||
cursor: string | null; | ||
hasNextPage: boolean; | ||
links: ZkSendLink[]; | ||
}>; | ||
export interface LinkAssets { | ||
balances: { | ||
coinType: string; | ||
amount: bigint; | ||
}[]; | ||
nfts: { | ||
objectId: string; | ||
type: string; | ||
version: string; | ||
digest: string; | ||
}[]; | ||
coins: { | ||
objectId: string; | ||
type: string; | ||
version: string; | ||
digest: string; | ||
}[]; | ||
} | ||
export declare function isClaimTransaction(txb: TransactionBlock, options: { | ||
packageId: string; | ||
}): boolean; | ||
export declare function getAssetsFromTxnBlock({ transactionBlock, address, isSent, }: { | ||
transactionBlock: SuiTransactionBlockResponse; | ||
address: string; | ||
isSent: boolean; | ||
}): LinkAssets; | ||
export declare function ownedAfterChange(objectChange: SuiObjectChange, address: string): objectChange is Extract<SuiObjectChange, { | ||
type: 'created' | 'transferred' | 'mutated'; | ||
}>; | ||
export declare function isOwner(owner: ObjectOwner, address: string): owner is { | ||
AddressOwner: string; | ||
}; |
@@ -1,110 +0,2 @@ | ||
import "@mysten/sui.js/graphql/schemas/2024-01"; | ||
import { bcs } from "@mysten/sui.js/bcs"; | ||
import { SuiGraphQLClient } from "@mysten/sui.js/graphql"; | ||
import { graphql } from "@mysten/sui.js/graphql/schemas/2024-01"; | ||
import { fromB64, normalizeSuiAddress } from "@mysten/sui.js/utils"; | ||
import { ZkSendLink } from "./claim.js"; | ||
const ListCreatedLinksQuery = graphql(` | ||
query listCreatedLinks($address: SuiAddress!, $function: String!, $cursor: String) { | ||
transactionBlocks( | ||
last: 10 | ||
before: $cursor | ||
filter: { signAddress: $address, function: $function, kind: PROGRAMMABLE_TX } | ||
) { | ||
pageInfo { | ||
startCursor | ||
hasPreviousPage | ||
} | ||
nodes { | ||
digest | ||
kind { | ||
__typename | ||
... on ProgrammableTransactionBlock { | ||
inputs(first: 10) { | ||
nodes { | ||
__typename | ||
... on Pure { | ||
bytes | ||
} | ||
} | ||
} | ||
transactions(first: 10) { | ||
nodes { | ||
__typename | ||
... on MoveCallTransaction { | ||
module | ||
functionName | ||
package | ||
arguments { | ||
__typename | ||
... on Input { | ||
ix | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
`); | ||
async function listCreatedLinks({ | ||
address, | ||
cursor, | ||
network, | ||
contract, | ||
...linkOptions | ||
}) { | ||
const gqlClient = new SuiGraphQLClient({ | ||
url: network === "testnet" ? "https://sui-testnet.mystenlabs.com/graphql" : "https://sui-mainnet.mystenlabs.com/graphql" | ||
}); | ||
const packageId = normalizeSuiAddress(contract.packageId); | ||
const page = await gqlClient.query({ | ||
query: ListCreatedLinksQuery, | ||
variables: { | ||
address, | ||
cursor, | ||
function: `${packageId}::zk_bag::new` | ||
} | ||
}); | ||
const transactionBlocks = page.data?.transactionBlocks; | ||
if (!transactionBlocks || page.errors?.length) { | ||
throw new Error("Failed to load created links"); | ||
} | ||
const links = transactionBlocks.nodes.map((node) => { | ||
if (node.kind?.__typename !== "ProgrammableTransactionBlock") { | ||
throw new Error("Invalid transaction block"); | ||
} | ||
const fn = node.kind.transactions.nodes.find( | ||
(fn2) => fn2.__typename === "MoveCallTransaction" && fn2.package === packageId && fn2.module === "zk_bag" && fn2.functionName === "new" | ||
); | ||
if (fn?.__typename !== "MoveCallTransaction") { | ||
return null; | ||
} | ||
const addressArg = fn.arguments[1]; | ||
if (addressArg.__typename !== "Input") { | ||
throw new Error("Invalid address argument"); | ||
} | ||
const input = node.kind.inputs.nodes[addressArg.ix]; | ||
if (input.__typename !== "Pure") { | ||
throw new Error("Expected Address input to be a Pure value"); | ||
} | ||
const address2 = bcs.Address.parse(fromB64(input.bytes)); | ||
return new ZkSendLink({ | ||
network, | ||
address: address2, | ||
contract, | ||
isContractLink: true, | ||
...linkOptions | ||
}); | ||
}).reverse().filter(Boolean); | ||
await Promise.all(links.map((link) => link.loadOwnedData())); | ||
return { | ||
cursor: transactionBlocks.pageInfo.startCursor, | ||
hasNextPage: transactionBlocks.pageInfo.hasPreviousPage, | ||
links | ||
}; | ||
} | ||
import { normalizeStructTag, normalizeSuiAddress, parseStructTag } from "@mysten/sui.js/utils"; | ||
function isClaimTransaction(txb, options) { | ||
@@ -138,6 +30,71 @@ let transfers = 0; | ||
} | ||
function getAssetsFromTxnBlock({ | ||
transactionBlock, | ||
address, | ||
isSent | ||
}) { | ||
const normalizedAddress = normalizeSuiAddress(address); | ||
const balances = []; | ||
const nfts = []; | ||
const coins = []; | ||
transactionBlock.balanceChanges?.forEach((change) => { | ||
const validAmountChange = isSent ? BigInt(change.amount) < 0n : BigInt(change.amount) > 0n; | ||
if (validAmountChange && isOwner(change.owner, normalizedAddress)) { | ||
balances.push({ | ||
coinType: normalizeStructTag(change.coinType), | ||
amount: BigInt(change.amount) | ||
}); | ||
} | ||
}); | ||
transactionBlock.objectChanges?.forEach((change) => { | ||
if ("objectType" in change) { | ||
const type = parseStructTag(change.objectType); | ||
if (type.address === normalizeSuiAddress("0x2") && type.module === "coin" && type.name === "Coin") { | ||
if (change.type === "created" || change.type === "transferred" || change.type === "mutated") { | ||
coins.push(change); | ||
} | ||
return; | ||
} | ||
} | ||
if (isObjectOwner(change, normalizedAddress, isSent) && (change.type === "created" || change.type === "transferred" || change.type === "mutated")) { | ||
nfts.push(change); | ||
} | ||
}); | ||
return { | ||
balances, | ||
nfts, | ||
coins | ||
}; | ||
} | ||
function getObjectOwnerFromObjectChange(objectChange, isSent) { | ||
if (isSent) { | ||
return "owner" in objectChange ? objectChange.owner : null; | ||
} | ||
return "recipient" in objectChange ? objectChange.recipient : null; | ||
} | ||
function isObjectOwner(objectChange, address, isSent) { | ||
const owner = getObjectOwnerFromObjectChange(objectChange, isSent); | ||
if (isSent) { | ||
return owner && typeof owner === "object" && "AddressOwner" in owner; | ||
} | ||
return ownedAfterChange(objectChange, address); | ||
} | ||
function ownedAfterChange(objectChange, address) { | ||
if (objectChange.type === "transferred" && isOwner(objectChange.recipient, address)) { | ||
return true; | ||
} | ||
if ((objectChange.type === "created" || objectChange.type === "mutated") && isOwner(objectChange.owner, address)) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
function isOwner(owner, address) { | ||
return owner && typeof owner === "object" && "AddressOwner" in owner && normalizeSuiAddress(owner.AddressOwner) === address; | ||
} | ||
export { | ||
getAssetsFromTxnBlock, | ||
isClaimTransaction, | ||
listCreatedLinks | ||
isOwner, | ||
ownedAfterChange | ||
}; | ||
//# sourceMappingURL=utils.js.map |
@@ -7,2 +7,3 @@ import type { TransactionArgument, TransactionBlock, TransactionObjectArgument } from '@mysten/sui.js/transactions'; | ||
} | ||
export declare const MAINNET_CONTRACT_IDS: ZkBagContractOptions; | ||
export declare class ZkBag<IDs> { | ||
@@ -9,0 +10,0 @@ #private; |
@@ -20,2 +20,7 @@ var __accessCheck = (obj, member, msg) => { | ||
var _package, _module; | ||
const MAINNET_CONTRACT_IDS = { | ||
packageId: "0x5bb7d0bb3240011336ca9015f553b2646302a4f05f821160344e9ec5a988f740", | ||
bagStoreId: "0x65b215a3f2a951c94313a89c43f0adbd2fd9ea78a0badf81e27d1c9868a8b6fe", | ||
bagStoreTableId: "0x616db54ca564660cd58e36a4548be68b289371ef2611485c62c374a60960084e" | ||
}; | ||
class ZkBag { | ||
@@ -108,4 +113,5 @@ constructor(packageAddress, ids) { | ||
export { | ||
MAINNET_CONTRACT_IDS, | ||
ZkBag | ||
}; | ||
//# sourceMappingURL=zk-bag.js.map |
{ | ||
"name": "@mysten/zksend", | ||
"version": "0.0.0-experimental-20240313015203", | ||
"version": "0.0.0-experimental-20240319165746", | ||
"description": "TODO: Write Description", | ||
@@ -5,0 +5,0 @@ "license": "Apache-2.0", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
516428
70
4733