Skip to content

Commit

Permalink
[@kadena/client-utils] Implement Marmalade functions (Feat) (#1484)
Browse files Browse the repository at this point in the history
* add marmalade generate contract script

* create token and create token id functions

* small refactor to create token id and create token

* mint token functionality

* transfer-create function

* get balance function

* refactor createToken

* index file

* added unit testing for createId, create and mint tokens

* integration testing; lint and style fixes; index file for marmalade

* removed unecessary exports

* omit sign in dirty reads

* changeset file

* marmalade integration testing init

* make chain argument declared at top;

* ci changes: add apt-get update

* default chain 0

* unit testing to meet threshold

* lint fixes

* fix: minor fixes and comments resolutions

* fix: adjusted precision type from number to Pact Int

* fix: lint fixes

* chore: update the create token capability signature

* [@kadena/client-utils] Implement Marmalade functions - additions (#1945)

* chore: added more maramalade util functions

* chore: align create-token with other functions

* fix: linting issues

* chore: add update-uri function

* chore: format files

* [@kadena/client-utils] Implement Marmalade functions - Sale functions (#2055)

* chore: added more maramalade util functions

* chore: align create-token with other functions

* fix: linting issues

* feat: added sale functions and tests; chore: updated and cleaned read functions

* chore: linting

* chore: added script to setup test environment before executing integration tests

* chore: lint setup marmalade test env file

* chore: gnerate marmalade-v2.policy-manager types

* chore: check if conventional and dutch auction have been whitelisted

* chore: remove whitelisting sale from test setup script

* chore: wait for block time helper function

* chore: fix lint issue

* chore: enforce failure uri guard if not updatable when using guard policy

* chore: rename config to updatableURI

* chore: remove guard on local calls

* feat: added collection-policy functions

* chore: adjust the auction end date in tests

* chore: cleanup and stability improvements

* fix: more time

* chore: simplify local call functions

* chore: linting

* chore: get quote function and reorganize imports (#2187)

---------

Co-authored-by: Ivan Magaš <imagas96@gmail.com>
  • Loading branch information
nil-amrutlal-dept and wooglie committed May 29, 2024
1 parent 07ec969 commit 381a766
Show file tree
Hide file tree
Showing 45 changed files with 6,545 additions and 45 deletions.
5 changes: 5 additions & 0 deletions .changeset/sour-gorillas-raise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@kadena/client-utils': patch
---

Added marmalade functions and correspondent integration testing
6 changes: 4 additions & 2 deletions packages/libs/client-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,11 @@
"lint:fmt": "prettier . --cache --check",
"lint:pkg": "lint-package",
"lint:src": "eslint src --ext .js,.ts",
"pactjs:generate:contract": "pactjs contract-generate --contract coin --api https://api.testnet.chainweb.com/chainweb/0.0/testnet04/chain/0/pact",
"pactjs:generate:contract": "pactjs contract-generate --contract coin --contract marmalade-v2.ledger --contract marmalade-v2.collection-policy-v1 --contract marmalade-v2.policy-manager --contract marmalade-sale.conventional-auction --contract marmalade-sale.dutch-auction --api https://api.chainweb.com/chainweb/0.0/mainnet01/chain/8/pact",
"test": "vitest run",
"test:integration": "vitest run -c ./vitest.integration.config.ts",
"test:integration": "pnpm run test:integration:setup && vitest run -c ./vitest.integration.config.ts",
"test:integration:local": "vitest run -c ./vitest.integration.config.ts",
"test:integration:setup": "pnpm run build && ts-node src/scripts/setup-marmalade-test-env.ts",
"test:watch": "vitest"
},
"dependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,376 @@
import type { ChainId } from '@kadena/client';
import { createSignWithKeypair } from '@kadena/client';
import { PactNumber } from '@kadena/pactjs';
import type { IPactInt } from '@kadena/types';
import { describe, expect, it } from 'vitest';
import {
createCollection,
createCollectionId,
createToken,
createTokenId,
getCollection,
getCollectionToken,
getTokenBalance,
mintToken,
} from '../marmalade';
import type { ICreateTokenPolicyConfig } from '../marmalade/config';
import { NetworkIds } from './support/NetworkIds';
import { withStepFactory } from './support/helpers';
import { sourceAccount } from './test-data/accounts';

let tokenId: string | undefined;
let collectionId: string | undefined;
let collectionName: string | undefined;
const collectionSize: IPactInt = { int: '0' };
const chainId = '0' as ChainId;

const inputs = {
policyConfig: {
collection: true,
} as ICreateTokenPolicyConfig,
collection: {
collectionId,
},
chainId,
precision: { int: '0' },
uri: Math.random().toString(),
policies: ['marmalade-v2.collection-policy-v1'],
creator: {
account: sourceAccount.account,
keyset: {
keys: [sourceAccount.publicKey],
pred: 'keys-all' as const,
},
},
};
const config = {
host: 'http://127.0.0.1:8080',
defaults: {
networkId: 'development',
},
sign: createSignWithKeypair([sourceAccount]),
};

describe('createCollectionId', () => {
it('should return a collection id', async () => {
collectionName = `Test Collection #${Math.random().toString()}`;

collectionId = await createCollectionId({
collectionName,
chainId,
operator: {
keyset: {
keys: [sourceAccount.publicKey],
pred: 'keys-all' as const,
},
},
networkId: config.defaults.networkId,
host: config.host,
});

expect(collectionId).toBeDefined();
expect(collectionId).toMatch(/^collection:.{43}$/);

inputs.collection.collectionId = collectionId;
});
});

describe('createCollection', () => {
it('should create a collection', async () => {
const withStep = withStepFactory();

const result = await createCollection(
{
id: collectionId as string,
name: collectionName as string,
size: collectionSize,
operator: inputs.creator,
chainId,
},
config,
)
.on(
'sign',
withStep((step, tx) => {
expect(step).toBe(1);
expect(tx.sigs).toHaveLength(1);
expect(tx.sigs[0].sig).toBeTruthy();
}),
)
.on(
'preflight',
withStep((step, prResult) => {
expect(step).toBe(2);
if (prResult.result.status === 'failure') {
expect(prResult.result.status).toBe('success');
} else {
expect(prResult.result.data).toBe(true);
}
}),
)
.on(
'submit',
withStep((step, trDesc) => {
expect(step).toBe(3);
expect(trDesc.networkId).toBe(NetworkIds.development);
expect(trDesc.chainId).toBe(chainId);
expect(trDesc.requestKey).toBeTruthy();
}),
)
.on(
'listen',
withStep((step, sbResult) => {
expect(step).toBe(4);
if (sbResult.result.status === 'failure') {
expect(sbResult.result.status).toBe('success');
} else {
expect(sbResult.result.data).toBe(true);
}
}),
)
.execute();

expect(result).toBe(true);
});
});

describe('createTokenId', () => {
it('should return a token id', async () => {
tokenId = await createTokenId({
...inputs,
networkId: config.defaults.networkId,
host: config.host,
});

expect(tokenId).toBeDefined();
expect(tokenId).toMatch(/^t:.{43}$/);
});
});

describe('createToken', () => {
it('should create a token with policy', async () => {
const withStep = withStepFactory();

const tokenId = await createTokenId({
...inputs,
networkId: config.defaults.networkId,
host: config.host,
});

expect(tokenId).toBeDefined();
expect(tokenId).toMatch(/^t:.{43}$/);

const result = await createToken(
{
...inputs,
tokenId: tokenId as string,
},
config,
)
.on(
'sign',
withStep((step, tx) => {
expect(step).toBe(1);
expect(tx.sigs).toHaveLength(1);
expect(tx.sigs[0].sig).toBeTruthy();
}),
)
.on(
'preflight',
withStep((step, prResult) => {
expect(step).toBe(2);
if (prResult.result.status === 'failure') {
expect(prResult.result.status).toBe('success');
} else {
expect(prResult.result.data).toBe(true);
}
}),
)
.on(
'submit',
withStep((step, trDesc) => {
expect(step).toBe(3);
expect(trDesc.networkId).toBe(NetworkIds.development);
expect(trDesc.chainId).toBe(chainId);
expect(trDesc.requestKey).toBeTruthy();
}),
)
.on(
'listen',
withStep((step, sbResult) => {
expect(step).toBe(4);
if (sbResult.result.status === 'failure') {
expect(sbResult.result.status).toBe('success');
} else {
expect(sbResult.result.data).toBe(true);
}
}),
)
.execute();

expect(result).toBe(true);
});
});

describe('mintToken', () => {
it('should mint a token', async () => {
const withStep = withStepFactory();

const result = await mintToken(
{
...inputs,
tokenId: tokenId as string,
accountName: sourceAccount.account,
guard: {
account: sourceAccount.account,
keyset: {
keys: [sourceAccount.publicKey],
pred: 'keys-all' as const,
},
},
amount: new PactNumber(1).toPactDecimal(),
},
config,
)
.on(
'sign',
withStep((step, tx) => {
expect(step).toBe(1);
expect(tx.sigs).toHaveLength(1);
expect(tx.sigs[0].sig).toBeTruthy();
}),
)
.on(
'preflight',
withStep((step, prResult) => {
expect(step).toBe(2);
if (prResult.result.status === 'failure') {
expect(prResult.result.status).toBe('success');
} else {
expect(prResult.result.data).toBe(true);
}
}),
)
.on(
'submit',
withStep((step, trDesc) => {
expect(step).toBe(3);
expect(trDesc.networkId).toBe(NetworkIds.development);
expect(trDesc.chainId).toBe(chainId);
expect(trDesc.requestKey).toBeTruthy();
}),
)
.on(
'listen',
withStep((step, sbResult) => {
expect(step).toBe(4);
if (sbResult.result.status === 'failure') {
expect(sbResult.result.status).toBe('success');
} else {
expect(sbResult.result.data).toBe(true);
}
}),
)
.execute();

expect(result).toBe(true);

const balance = await getTokenBalance({
accountName: sourceAccount.account,
chainId,
tokenId: tokenId as string,
networkId: config.defaults.networkId,
host: config.host,
});

expect(balance).toBe(1);
});
it('should throw error when non-existent token is minted', async () => {
const nonExistingTokenId = 'non-existing-token';
const task = mintToken(
{
...inputs,
tokenId: nonExistingTokenId,
accountName: sourceAccount.account,
guard: {
account: sourceAccount.account,
keyset: {
keys: [sourceAccount.publicKey],
pred: 'keys-all' as const,
},
},
amount: new PactNumber(1).toPactDecimal(),
},
config,
);

await expect(() => task.execute()).rejects.toThrowError(
new Error('with-read: row not found: non-existing-token'),
);
});
});

describe('getCollection', () => {
it('should get the collection details', async () => {
const result = await getCollection({
chainId,
collectionId: collectionId as string,
networkId: config.defaults.networkId,
host: config.host,
});

expect(result).toStrictEqual({
id: collectionId,
'max-size': {
int: Number(collectionSize.int),
},
name: collectionName,
'operator-guard': inputs.creator.keyset,
size: {
int: 1,
},
});
});
it('should throw an error if token does not exist', async () => {
const nonExistingTokenId = 'non-existing-collection';
const task = getCollection({
collectionId: nonExistingTokenId,
chainId,
networkId: config.defaults.networkId,
host: config.host,
});

await expect(() => Promise.resolve(task)).rejects.toThrowError(
new Error(`read: row not found: non-existing-collection`),
);
});
});

describe('getCollectionToken', () => {
it('should get the collection token details', async () => {
const result = await getCollectionToken({
chainId,
tokenId: tokenId as string,
networkId: config.defaults.networkId,
host: config.host,
});

expect(result).toStrictEqual({
id: tokenId,
'collection-id': collectionId,
});
});
it('should throw an error if token does not exist', async () => {
const nonExistingTokenId = 'non-existing-token';
const task = getCollectionToken({
chainId,
tokenId: nonExistingTokenId,
networkId: config.defaults.networkId,
host: config.host,
});

await expect(() => Promise.resolve(task)).rejects.toThrowError(
new Error(`read: row not found: non-existing-token`),
);
});
});
Loading

0 comments on commit 381a766

Please sign in to comment.