Skip to content

Commit

Permalink
Merge pull request #11 from metaplex-foundation/fix/v2-reroll
Browse files Browse the repository at this point in the history
Path refactor for v2
  • Loading branch information
potatoe-dev authored Nov 20, 2024
2 parents 371c242 + 49b435a commit a816465
Show file tree
Hide file tree
Showing 18 changed files with 1,117 additions and 52 deletions.
2 changes: 1 addition & 1 deletion clients/js/src/generated/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
* @see https://github.com/metaplex-foundation/kinobi
*/

export * from './path';
export * from './internalPath';
24 changes: 24 additions & 0 deletions clients/js/src/generated/types/internalPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* This code was AUTOGENERATED using the kinobi library.
* Please DO NOT EDIT THIS FILE, instead use visitors
* to add features, then rerun kinobi to update it.
*
* @see https://github.com/metaplex-foundation/kinobi
*/

import { Serializer, scalarEnum } from '@metaplex-foundation/umi/serializers';

export enum InternalPath {
NoRerollMetadata,
}

export type InternalPathArgs = InternalPath;

export function getInternalPathSerializer(): Serializer<
InternalPathArgs,
InternalPath
> {
return scalarEnum<InternalPath>(InternalPath, {
description: 'InternalPath',
}) as Serializer<InternalPathArgs, InternalPath>;
}
22 changes: 0 additions & 22 deletions clients/js/src/generated/types/path.ts

This file was deleted.

1 change: 1 addition & 0 deletions clients/js/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './generated';
export * from './plugin';
export * from './path';
27 changes: 27 additions & 0 deletions clients/js/src/path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { InternalPath } from './generated';

enum CustomPath {
RerollMetadata = 16,
}

export const Path = {
...InternalPath,
...CustomPath,
} as const;

// eslint-disable-next-line @typescript-eslint/no-redeclare
export type Path = InternalPath | CustomPath;

export function buildPath(features: Path[]) {
let path = 0;

// eslint-disable-next-line no-restricted-syntax
for (const feature of features) {
if (feature !== Path.RerollMetadata) {
// eslint-disable-next-line no-bitwise
path |= 1 << feature;
}
}

return path;
}
255 changes: 251 additions & 4 deletions clients/js/test/v1/capture.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
transfer,
} from '@metaplex-foundation/mpl-core';
import {
buildPath,
captureV1,
EscrowV1,
fetchEscrowV1,
Expand Down Expand Up @@ -76,7 +77,7 @@ test('it can swap tokens for an asset with reroll', async (t) => {
min: 0,
amount: 5,
feeAmount: 1,
path: Path.RerollMetadata,
path: buildPath([Path.RerollMetadata]),
solFeeAmount: 1000000n,
}).sendAndConfirm(umi);

Expand All @@ -94,7 +95,7 @@ test('it can swap tokens for an asset with reroll', async (t) => {
amount: 5n,
feeAmount: 1n,
count: 1n,
path: Path.RerollMetadata,
path: buildPath([Path.RerollMetadata]),
bump: escrow[1],
solFeeAmount: 1_000_000n,
});
Expand Down Expand Up @@ -154,6 +155,134 @@ test('it can swap tokens for an asset with reroll', async (t) => {
t.regex(assetAfter.uri, uriRegex);
});

test('it can swap tokens for an asset without reroll', async (t) => {
// Given a Umi instance using the project's plugin.
const umi = await createUmi();
const feeLocation = generateSigner(umi);
const { assets, collection } = await createCoreCollection(umi);
const tokenMint = generateSigner(umi);
await createFungible(umi, {
name: 'Test Token',
uri: 'www.fungible.com',
sellerFeeBasisPoints: {
basisPoints: 0n,
identifier: '%',
decimals: 2,
},
mint: tokenMint,
}).sendAndConfirm(umi);

await mintV1(umi, {
mint: tokenMint.publicKey,
tokenStandard: TokenStandard.Fungible,
tokenOwner: umi.identity.publicKey,
amount: 1000,
}).sendAndConfirm(umi);

const escrow = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [
string({ size: 'variable' }).serialize('escrow'),
publicKeySerializer().serialize(collection.publicKey),
]);

// Transfer the assets to the escrow.
// eslint-disable-next-line no-restricted-syntax
for (const asset of assets) {
// eslint-disable-next-line no-await-in-loop
await transfer(umi, {
asset,
collection,
newOwner: escrow,
}).sendAndConfirm(umi);
}

await initEscrowV1(umi, {
escrow,
collection: collection.publicKey,
token: tokenMint.publicKey,
feeLocation: feeLocation.publicKey,
name: 'Test Escrow',
uri: 'www.test.com/',
max: 9,
min: 0,
amount: 5,
feeAmount: 1,
path: buildPath([Path.NoRerollMetadata]),
solFeeAmount: 1000000n,
}).sendAndConfirm(umi);

const escrowData = await fetchEscrowV1(umi, escrow);

t.like(escrowData, <EscrowV1>{
publicKey: publicKey(escrow),
collection: collection.publicKey,
token: tokenMint.publicKey,
feeLocation: feeLocation.publicKey,
name: 'Test Escrow',
uri: 'www.test.com/',
max: 9n,
min: 0n,
amount: 5n,
feeAmount: 1n,
count: 1n,
path: buildPath([Path.NoRerollMetadata]),
bump: escrow[1],
solFeeAmount: 1_000_000n,
});

const userTokenBefore = await fetchDigitalAssetWithAssociatedToken(
umi,
tokenMint.publicKey,
umi.identity.publicKey
);
t.deepEqual(userTokenBefore.token.amount, 1000n);
try {
await fetchDigitalAssetWithAssociatedToken(
umi,
tokenMint.publicKey,
publicKey(escrow)
);
t.fail('Escrow token account should not exist');
} catch (e) {
t.is(e.name, 'AccountNotFoundError');
}

const assetBefore = await fetchAsset(umi, assets[0].publicKey);
t.is(assetBefore.owner, publicKey(escrow));

await captureV1(umi, {
owner: umi.identity,
escrow,
asset: assets[0].publicKey,
collection: collection.publicKey,
feeProjectAccount: escrowData.feeLocation,
token: tokenMint.publicKey,
}).sendAndConfirm(umi);

const escrowTokenAfter = await fetchDigitalAssetWithAssociatedToken(
umi,
tokenMint.publicKey,
publicKey(escrow)
);
t.deepEqual(escrowTokenAfter.token.amount, 5n);
const userTokenAfter = await fetchDigitalAssetWithAssociatedToken(
umi,
tokenMint.publicKey,
umi.identity.publicKey
);
t.deepEqual(userTokenAfter.token.amount, 994n);
const feeTokenAfter = await fetchDigitalAssetWithAssociatedToken(
umi,
tokenMint.publicKey,
escrowData.feeLocation
);
t.deepEqual(feeTokenAfter.token.amount, 1n);
const assetAfter = await fetchAsset(umi, assets[0].publicKey);
t.is(assetAfter.owner, umi.identity.publicKey);

// Make sure the URI has not changed.
t.is(assetAfter.uri, 'https://example.com/asset');
});

test('it can swap tokens for an asset as UpdateDelegate with reroll', async (t) => {
// Given a Umi instance using the project's plugin.
const umi = await createUmi();
Expand Down Expand Up @@ -205,7 +334,7 @@ test('it can swap tokens for an asset as UpdateDelegate with reroll', async (t)
min: 0,
amount: 5,
feeAmount: 1,
path: Path.RerollMetadata,
path: buildPath([Path.RerollMetadata]),
solFeeAmount: 1000000n,
}).sendAndConfirm(umi);

Expand All @@ -232,7 +361,7 @@ test('it can swap tokens for an asset as UpdateDelegate with reroll', async (t)
amount: 5n,
feeAmount: 1n,
count: 1n,
path: Path.RerollMetadata,
path: buildPath([Path.RerollMetadata]),
bump: escrow[1],
solFeeAmount: 1_000_000n,
});
Expand Down Expand Up @@ -272,3 +401,121 @@ test('it can swap tokens for an asset as UpdateDelegate with reroll', async (t)
const uriRegex = new RegExp(`${escrowData.uri}\\d+\\.json`);
t.regex(assetAfter.uri, uriRegex);
});

test('it can swap tokens for an asset as UpdateDelegate without reroll', async (t) => {
// Given a Umi instance using the project's plugin.
const umi = await createUmi();
const feeLocation = generateSigner(umi);
const { assets, collection } = await createCoreCollection(umi);
const tokenMint = generateSigner(umi);
await createFungible(umi, {
name: 'Test Token',
uri: 'www.fungible.com',
sellerFeeBasisPoints: {
basisPoints: 0n,
identifier: '%',
decimals: 2,
},
mint: tokenMint,
}).sendAndConfirm(umi);

await mintV1(umi, {
mint: tokenMint.publicKey,
tokenStandard: TokenStandard.Fungible,
tokenOwner: umi.identity.publicKey,
amount: 1000,
}).sendAndConfirm(umi);

const escrow = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [
string({ size: 'variable' }).serialize('escrow'),
publicKeySerializer().serialize(collection.publicKey),
]);

// Transfer the assets to the escrow.
// eslint-disable-next-line no-restricted-syntax
for (const asset of assets) {
// eslint-disable-next-line no-await-in-loop
await transfer(umi, {
asset,
collection,
newOwner: escrow,
}).sendAndConfirm(umi);
}

await initEscrowV1(umi, {
escrow,
collection: collection.publicKey,
token: tokenMint.publicKey,
feeLocation: feeLocation.publicKey,
name: 'Test Escrow',
uri: 'www.test.com/',
max: 9,
min: 0,
amount: 5,
feeAmount: 1,
path: buildPath([Path.NoRerollMetadata]),
solFeeAmount: 1000000n,
}).sendAndConfirm(umi);

await addCollectionPlugin(umi, {
collection: collection.publicKey,
plugin: {
type: 'UpdateDelegate',
additionalDelegates: [],
authority: { type: 'Address', address: publicKey(escrow) },
},
}).sendAndConfirm(umi);

const escrowData = await fetchEscrowV1(umi, escrow);

t.like(escrowData, <EscrowV1>{
publicKey: publicKey(escrow),
collection: collection.publicKey,
token: tokenMint.publicKey,
feeLocation: feeLocation.publicKey,
name: 'Test Escrow',
uri: 'www.test.com/',
max: 9n,
min: 0n,
amount: 5n,
feeAmount: 1n,
count: 1n,
path: buildPath([Path.NoRerollMetadata]),
bump: escrow[1],
solFeeAmount: 1_000_000n,
});

await captureV1(umi, {
owner: umi.identity,
authority: escrow,
escrow,
asset: assets[0].publicKey,
collection: collection.publicKey,
feeProjectAccount: escrowData.feeLocation,
token: tokenMint.publicKey,
}).sendAndConfirm(umi);

const escrowTokenAfter = await fetchDigitalAssetWithAssociatedToken(
umi,
tokenMint.publicKey,
publicKey(escrow)
);
t.deepEqual(escrowTokenAfter.token.amount, 5n);
const userTokenAfter = await fetchDigitalAssetWithAssociatedToken(
umi,
tokenMint.publicKey,
umi.identity.publicKey
);
t.deepEqual(userTokenAfter.token.amount, 994n);
const feeTokenAfter = await fetchDigitalAssetWithAssociatedToken(
umi,
tokenMint.publicKey,
escrowData.feeLocation
);
t.deepEqual(feeTokenAfter.token.amount, 1n);
const assetAfter = await fetchAsset(umi, assets[0].publicKey);
t.is(assetAfter.owner, umi.identity.publicKey);

// Make sure the URI has not changed.
t.is(assetAfter.uri, 'https://example.com/asset');
});
Loading

0 comments on commit a816465

Please sign in to comment.