Skip to content

Commit

Permalink
[zksend] Add isClaimTransaction helper (MystenLabs#16637)
Browse files Browse the repository at this point in the history
## Description 

Describe the changes or additions included in this PR.

## Test Plan 

How did you test the new or updated feature?

---
If your changes are not user-facing and do not break anything, you can
skip the following section. Otherwise, please briefly describe what has
changed under the Release Notes section.

### Type of Change (Check all that apply)

- [ ] protocol change
- [ ] user-visible impact
- [ ] breaking change for a client SDKs
- [ ] breaking change for FNs (FN binary must upgrade)
- [ ] breaking change for validators or node operators (must upgrade
binaries)
- [ ] breaking change for on-chain data layout
- [ ] necessitate either a data wipe or data migration

### Release notes
  • Loading branch information
hayes-mysten authored Mar 12, 2024
1 parent 667d265 commit b828322
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 24 deletions.
5 changes: 5 additions & 0 deletions .changeset/moody-hounds-pretend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@mysten/zksend': patch
---

Add isClaimTransaction helper
2 changes: 1 addition & 1 deletion sdk/zksend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ export {
} from './links/builder.js';
export { ZkSendLink, type ZkSendLinkOptions } from './links/claim.js';
export { type ZkBagContractOptions } from './links/zk-bag.js';
export { listCreatedLinks } from './links/utils.js';
export { listCreatedLinks, isClaimTransaction } from './links/utils.js';
export * from './wallet.js';
export * from './channel/index.js';
49 changes: 26 additions & 23 deletions sdk/zksend/src/links/claim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ export type ZkSendLinkOptions = {

export class ZkSendLink {
#client: SuiClient;
#address: string;
#keypair?: Keypair;
address: string;
keypair?: Keypair;
creatorAddress?: string;
#initiallyOwnedObjects = new Set<string>();
#ownedObjects: Array<{
objectId: string;
Expand All @@ -85,7 +86,7 @@ export class ZkSendLink {
}> | null = null;
#gasCoin?: CoinStruct;
#hasSui = false;
#creatorAddress?: string;

#contract?: ZkBag<ZkBagContractOptions>;
#claimApi: string;
#network: 'mainnet' | 'testnet';
Expand All @@ -108,8 +109,8 @@ export class ZkSendLink {
}

this.#client = client;
this.#keypair = keypair;
this.#address = address ?? keypair!.toSuiAddress();
this.keypair = keypair;
this.address = address ?? keypair!.toSuiAddress();
this.#claimApi = claimApi;
this.#network = network;
this.#host = host;
Expand Down Expand Up @@ -267,21 +268,21 @@ export class ZkSendLink {
objects?: string[];
},
) {
if (!this.#keypair) {
if (!this.keypair) {
throw new Error('Cannot claim assets without links keypair');
}

const txb = this.createClaimTransaction(address, options);
if (!this.#contract || !this.#bagObjects) {
return this.#client.signAndExecuteTransactionBlock({
transactionBlock: txb,
signer: this.#keypair,
signer: this.keypair,
});
}

const { digest } = await this.#executeSponsoredTransactionBlock(
await this.#createSponsoredTransactionBlock(txb, address, this.#keypair.toSuiAddress()),
this.#keypair,
await this.#createSponsoredTransactionBlock(txb, address, this.keypair.toSuiAddress()),
this.keypair,
);

return this.#client.waitForTransactionBlock({ digest });
Expand Down Expand Up @@ -310,18 +311,18 @@ export class ZkSendLink {
throw new Error('Filtering claims is not supported for contract based links');
}

if (!this.#keypair && !reclaim) {
if (!this.keypair && !reclaim) {
throw new Error('Cannot claim assets without the links keypair');
}

const txb = new TransactionBlock();
const sender = reclaim ? address : this.#keypair!.toSuiAddress();
const sender = reclaim ? address : this.keypair!.toSuiAddress();
txb.setSender(sender);

const store = txb.object(this.#contract.ids.bagStoreId);

const [bag, proof] = reclaim
? this.#contract.reclaim(txb, { arguments: [store, this.#address] })
? this.#contract.reclaim(txb, { arguments: [store, this.address] })
: this.#contract.init_claim(txb, { arguments: [store] });

const objectsToTransfer = [];
Expand Down Expand Up @@ -378,7 +379,7 @@ export class ZkSendLink {

const to = txb.pure.address(newLinkKp.toSuiAddress());

this.#contract.update_receiver(txb, { arguments: [store, this.#address, to] });
this.#contract.update_receiver(txb, { arguments: [store, this.address, to] });

return {
url: newLink.getLink(),
Expand All @@ -395,7 +396,7 @@ export class ZkSendLink {
parentId: this.#contract.ids.bagStoreTableId,
name: {
type: 'address',
value: this.#address,
value: this.address,
},
});

Expand All @@ -406,6 +407,8 @@ export class ZkSendLink {
const itemIds: string[] | undefined = (bagField as any).data?.content?.fields?.value?.fields
?.item_ids.fields.contents;

this.creatorAddress = (bagField as any).data?.content?.fields?.value?.fields?.owner;

if (!itemIds) {
throw new Error('Invalid bag field');
}
Expand Down Expand Up @@ -571,13 +574,13 @@ export class ZkSendLink {
objects?: string[];
},
) {
if (!this.#keypair) {
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());
txb.setSender(this.keypair.toSuiAddress());
const coinTypes = new Set(
options?.coinTypes?.map((type) => normalizeStructTag(`0x2::coin::Coin<${type}>`)) ?? [],
);
Expand Down Expand Up @@ -607,8 +610,8 @@ export class ZkSendLink {
})
.map((object) => txb.object(object.objectId));

if (this.#gasCoin && this.#creatorAddress) {
txb.transferObjects([txb.gas], this.#creatorAddress);
if (this.#gasCoin && this.creatorAddress) {
txb.transferObjects([txb.gas], this.creatorAddress);
} else if (claimAll || coinTypes?.has(SUI_COIN_TYPE)) {
objectsToTransfer.push(txb.gas);
}
Expand All @@ -626,7 +629,7 @@ export class ZkSendLink {
do {
const ownedObjects = await this.#client.getOwnedObjects({
cursor: nextCursor,
owner: this.#address,
owner: this.address,
options: {
showType: true,
},
Expand All @@ -648,7 +651,7 @@ export class ZkSendLink {

const coins = await this.#client.getCoins({
coinType: SUI_COIN_TYPE,
owner: this.#address,
owner: this.address,
});

this.#hasSui = coins.data.length > 0;
Expand All @@ -660,7 +663,7 @@ export class ZkSendLink {
limit: 1,
order: 'ascending',
filter: {
ToAddress: this.#address,
ToAddress: this.address,
},
options: {
showObjectChanges: true,
Expand All @@ -669,12 +672,12 @@ export class ZkSendLink {
});

result.data[0]?.objectChanges?.forEach((objectChange) => {
if (ownedAfterChange(objectChange, this.#address)) {
if (ownedAfterChange(objectChange, this.address)) {
this.#initiallyOwnedObjects.add(normalizeSuiObjectId(objectChange.objectId));
}
});

this.#creatorAddress = result.data[0]?.transaction?.data.sender;
this.creatorAddress = result.data[0]?.transaction?.data.sender;
}
}

Expand Down
41 changes: 41 additions & 0 deletions sdk/zksend/src/links/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { bcs } from '@mysten/sui.js/bcs';
import type { SuiClient } from '@mysten/sui.js/client';
import { SuiGraphQLClient } from '@mysten/sui.js/graphql';
import { graphql } from '@mysten/sui.js/graphql/schemas/2024-01';
import type { TransactionBlock } from '@mysten/sui.js/transactions';
import { fromB64, normalizeSuiAddress } from '@mysten/sui.js/utils';

import { ZkSendLink } from './claim.js';
Expand Down Expand Up @@ -150,3 +151,43 @@ export async function listCreatedLinks({
links,
};
}

export function isClaimTransaction(
txb: TransactionBlock,
options: {
packageId: string;
},
) {
let transfers = 0;

for (const tx of txb.blockData.transactions) {
switch (tx.kind) {
case 'TransferObjects':
// Ensure that we are only transferring results of a claim
if (!tx.objects.every((o) => o.kind === 'Result' || o.kind === 'NestedResult')) {
return false;
}
transfers++;
break;
case 'MoveCall':
const [packageId, module, fn] = tx.target.split('::');

if (packageId !== options.packageId) {
return false;
}

if (module !== 'zk_bag') {
return false;
}

if (fn !== 'init_claim' && fn !== 'reclaim' && fn !== 'claim' && fn !== 'finalize') {
return false;
}
break;
default:
return false;
}
}

return transfers === 1;
}

0 comments on commit b828322

Please sign in to comment.