diff --git a/.env.example b/.env.example
new file mode 100644
index 00000000..059019c4
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,5 @@
+PERMISSION=active
+NETWORK_NAME=mainnet
+ACTOR=eosaccountname
+PRIVATE_KEY=private_key_here
+PUBLIC_KEY=public_key_here
diff --git a/.gitignore b/.gitignore
index f6e9bfd2..f31a036f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,8 +17,10 @@ lerna-debug.log*
.env.development.local
.env.test.local
.env.production.local
+.env.testnet
+.env.mainnet
.envrc
# ignore lock files other then bun.
package-lock.json
-yarn.lock
\ No newline at end of file
+yarn.lock
diff --git a/README.md b/README.md
index c193b397..09e8f8f5 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,16 @@ If you want to add contributions to this repository, please follow the instructi
## 🏠Local Development
Follow the docs to Set Up Your Local Development Environment to contribute to the framework and documentation.
+### Testing
+#### `.env`
+
+To run the tests locally, you will need to provide an EOS account that can be used to make transactions on your behalf.
+Copy the `.env.example` file to `.env.testnet` and fill in the required parameters.
+
+#### PowerUp
+
+You might need to power up your account, either by adding some Native Token to your account or EFX tokens.
+You can powerup your account at the following link: https://monitor4.jungletestnet.io/
diff --git a/bun.lockb b/bun.lockb
index dc964540..8eb3799c 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/docs/pages/docs/compatibility.mdx b/docs/pages/docs/compatibility.mdx
index 2f3dc4f9..941d98c5 100644
--- a/docs/pages/docs/compatibility.mdx
+++ b/docs/pages/docs/compatibility.mdx
@@ -1,3 +1,5 @@
# Compatibility
-The Effect AI SDK is compatible with all Node.js environments, including browsers and React Native.
\ No newline at end of file
+The Effect AI SDK is compatible with all Node.js environments, including browsers and React Native.
+It might be necessary to use a particular `fetch` polyfill in some environments.
+You can read more about it [here](./glossary/client_options.mdx)
diff --git a/docs/pages/docs/getting-started.mdx b/docs/pages/docs/getting-started.mdx
deleted file mode 100644
index 9a109e60..00000000
--- a/docs/pages/docs/getting-started.mdx
+++ /dev/null
@@ -1,14 +0,0 @@
-# Getting Started [Get started with the SDK in just a few lines of code.]
-
-## Overview
-
-## Installation
-
-## Quick Start
-
-### 1.
-
-## Live example
-
-TODO:: insert example
-
diff --git a/docs/pages/docs/gettingStarted.mdx b/docs/pages/docs/gettingStarted.mdx
new file mode 100644
index 00000000..6cc33084
--- /dev/null
+++ b/docs/pages/docs/gettingStarted.mdx
@@ -0,0 +1,157 @@
+# Getting Started [Get started with the SDK in just a few lines of code.]
+
+## Overview
+
+## Installation
+
+Use your favorite package manager to install the SDK.
+
+ :::code-group
+
+ ```bash [npm]
+
+ npm i @effectai/effect-js
+ ```
+
+ ```bash [bun]
+ bun i @effectai/effect-js
+ ```
+
+ ```bash [pnpm]
+ pnpm i @effectai/effect-js
+ ```
+ :::
+## Quick Start
+
+### 1. Import the SDK
+
+```ts
+import { createClient } from '@effectai/effect-js'
+```
+
+### 2. Instantiate the EffectAI Client
+
+Here the handy dandy `createClient` function is used to create a client instance.
+It takes two optional arguments, the first one is the network configuration, the second one is the options object.
+You can read more about the options object in the [ClientOptions](#client-options) section.
+
+```ts
+// This would be ideal, the big question is if using mainnet should be explicitly passed or not.
+const client = createClient()
+
+// But currently it is like this:
+import { jungle4 } from '@effectai/effect-js'
+// Atleast the opts ({}) object is now optional
+const client = createClient(jungle4)
+```
+
+### 3. Interact with the Client
+
+The sdk is built using a stateless model, meaning that all the functions are pure and do not mutate the client object.
+This means, that you need to creat one client, and you can pass that client as an argument to functions to make transactions.
+
+Here is an example of how to get the balance of an account, assuming that the client is already set up as described above.
+
+```ts
+import { getVAccounts, getAccountById } from "@effectai/effect-js";
+import { Name } from "@wharfkit/antelope";
+
+const actor = Name.from("forcedev1234");
+// Notice we are passing the clinet as an argument to the function.
+const [vacc] = await getVAccounts({ client, actor });
+
+console.log(vacc)
+/**
+{
+ id: 24,
+ nonce: 0,
+ address: [ "name", "forcedev1234" ],
+ balance: {
+ quantity: "0.0000 EFX",
+ contract: "efxtoken1112",
+ },
+}
+*/
+```
+
+### 4. Set up wallet and session
+
+If you want to make transactions, such as creating a new account, creating a new tasks, or transfering tokens, you need to set up a wallet and a session.
+The wallet plguin is an interface that allows you to sign transactions and interact with the blockchain.
+The SDK comes with a few wallet plugins out of the box, but you can also create your own.
+
+In this example we will use the `WalletPluginPrivateKey` plugin, which requres a private key to be passed in.
+
+Afterwards we can set up the Session object, which allows us to specify other parameters like the actor, permission, and the blockchain netork.
+
+Last but not least we connect the session to the client, and the client will be ready to use.
+
+```ts
+import { createBatch, createVAccount } from "@effectai/effect-js";
+import { Session } from "@wharfkit/session";
+import { WalletPluginPrivateKey } from "@wharfkit/wallet-plugin-privatekey"
+
+const walletPlugin = new WalletPluginPrivateKey("your_private_key_here")
+
+const session = new Session({
+ actor: "forcedev1234",
+ permission: "active",
+ walletPlugin,
+ chain: {
+ id: jungle4.eosChainId,
+ url: jungle4.eosRpcUrl,
+ },
+})
+
+// Connect the session to the client.
+await session.setSession(session)
+
+// Now you can use the client to make authenticated transactions.
+const account = Name.from("efxforce1112");
+await createVAccount({ client, account });
+```
+
+## Full example
+
+```ts
+import { Session, Name } from "@wharfkit/session";
+import { WalletPluginPrivateKey } from "@wharfkit/wallet-plugin-privatekey";
+import {
+ createClient,
+ jungle4,
+ eos, // Use `eos` to use mainnet
+ getVAccounts,
+ createVAccount,
+} from "@effectai/effect-js";
+
+const client = createClient({ network: jungle4 });
+
+// Make unauthenticated requests
+const actor = Name.from("forcedev1234");
+const [vacc] = await getVAccounts({ client, actor });
+
+// Set up wallet with privatekey
+const walletPlugin = new WalletPluginPrivateKey("your_private_key_here");
+
+// Set up session with wallet and chain
+const session = new Session({
+ actor: "forcedev1234",
+ permission: "active",
+ walletPlugin,
+ chain: {
+ id: jungle4.eosChainId,
+ url: jungle4.eosRpcUrl,
+ },
+});
+
+// Connect session to client
+await client.setSession(session);
+
+// Now you can use the client to make authenticated transactions.
+const account = Name.from("efxforce1112");
+await createVAccount({ client, account });
+```
+
+Now that we have a basic understaning of how to set up the client, we can move on to more advanced topics.
+Read more on the following pages.
+
diff --git a/docs/pages/docs/glossary/clientOptions.mdx b/docs/pages/docs/glossary/clientOptions.mdx
new file mode 100644
index 00000000..d42ce0e8
--- /dev/null
+++ b/docs/pages/docs/glossary/clientOptions.mdx
@@ -0,0 +1,50 @@
+# Client Options
+
+The ClientOpts interface is used to define the options that can be passed to the EffectAI Client constructor.
+
+```typescript
+interface ClientOpts {
+ ipfsCacheDurationInMs?: number | null;
+ fetchProvider?: FetchProviderOptions;
+ cacheImplementation?: Cache;
+}
+```
+
+As we can see, the ClientOpts interface has three optional properties:
+
+## `ipfsCacheDurationIMs`
+
+This property is used to set the cache duration for the IPFS data.
+ The default value is 600_000 milliseconds; 10 minutes.
+
+## `fetchProfider`
+
+This property is used to set the fetch provider.
+This is needed because of the different runtimes availalbe to Java Script.
+For example, in older versions of Node.js, the fetch API is not available.
+For older versions of Node.js, you can use the [`node-fetch` ](https://github.com/node-fetch/node-fetch) package.
+
+Since Node.js v18.0.0, the fetch API is available by default.
+
+In the browser fetch is generally available, and is available on the `window.fetch` object.
+You can read more about it here: [MDN Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
+
+Other serverside Java Script runtimes such as [Bun](https://bun.sh/) and (Deno)[https://deno.com/] already have fetch available on the global `fetch` object.
+
+## `cacheImplementation`
+
+This property is used to set the cache implementation.
+There are three different cache mechanigms abailable in the EffectAI SDK.
+First of all, "IDBCache", "LocalStorageCache", "MemoryCache".
+Any of these can be passed to the `cacheImplementation` property while instanlizing the EffectAI Client.
+
+```typescript
+import { createClient, IDBCache, LocalStorageCache, MemoryCache } from '@effectai/sdk'
+const client = createClient({
+ cacheImplementation: new IDBCache() // or new LocalStorageCache() or new MemoryCache()
+})
+```
+
+# TODO: This needs to be changed when the `dev` branch is merged into main.
+[See Type](https://github.com/effectai/effect-js/blob/main/src/client.ts)
+
diff --git a/docs/pages/docs/guides/create-a-campaign.mdx b/docs/pages/docs/guides/create-a-campaign.mdx
deleted file mode 100644
index 8e1bb31f..00000000
--- a/docs/pages/docs/guides/create-a-campaign.mdx
+++ /dev/null
@@ -1,3 +0,0 @@
-## Creating Your First Campaign
-
-To create a campaign, you need to have a few things in place:
diff --git a/docs/pages/docs/guides/createACampaign.mdx b/docs/pages/docs/guides/createACampaign.mdx
new file mode 100644
index 00000000..e27149ea
--- /dev/null
+++ b/docs/pages/docs/guides/createACampaign.mdx
@@ -0,0 +1,90 @@
+## Creating Your First Campaign
+
+To create a campaign, you need to have a few things in place:
+
+
+# Create and upload campaign
+
+## Usage
+
+```ts [example.ts]
+import { createClient, createCampaign } from '@effectai/effect-js'
+
+const campaign: Mkcampaign = {
+ owner: ["name", "efxefxefxefx"],
+ content: {
+ field_0: 0,
+ field_1: "QmVKwq3bYM6cPW6kstpiq4WYckWRtdfJnzAmms2iMyGqQg",
+ },
+ max_task_time: 100,
+ reward: { quantity: "42.1234, EFX", contract: "efxtoken1112" },
+ qualis: [],
+ payer: "efxefxefxefx",
+};
+
+const data = {
+ version: 1.1,
+ title: "Labelstudio OCR (LAION)",
+ description:
+ "You are contributing to a dataset for conversational style chatbots.",
+ instructions: "Some instructions here",
+ template: "
Template here
",
+ input_schema: null,
+ output_schema: null,
+ image: "",
+ category: "",
+ example_task: "",
+ estimated_time: 10,
+};
+
+const client = await testClientSession({ testEnvNetwork: jungle4 });
+const result = await createCampaign({ client, campaign, data });
+```
+
+## Returns
+
+Result is a transaction response object.
+
+```json
+response: {
+ transaction_id: "9d321af28b7354c5cbee6ee956ea3e6590228b48539a9f0cafc6a8ca5ffe0ca2",
+ processed: {
+ id: "9d321af28b7354c5cbee6ee956ea3e6590228b48539a9f0cafc6a8ca5ffe0ca2",
+ block_num: 137520447,
+ block_time: "2024-05-01T03:55:31.500",
+ producer_block_id: null,
+ receipt: [Object ...],
+ elapsed: 4854,
+ net_usage: 176,
+ scheduled: false,
+ action_traces: [
+ [Object ...]
+ ],
+ account_ram_delta: null,
+ except: null,
+ error_code: null,
+ },
+}
+```
+
+
+- **Type:** [`Promise>`](/docs/glossary/types#campaign)
+- **Description:** A list of campaigns.
+- **Properties:**
+ - **rows:** An array of campaigns.
+ - **more:** A boolean indicating if there are more campaigns to fetch.
+ - **next_key:** A string that can be used to fetch the next page of campaigns.
+
+## Parameters
+
+### client
+- **Type:** `Client`
+
+### limit (optional)
+- **Type:** `number`
+- **Default:** `10`
+
+### page (optional)
+- **Type:** `number`
+- **Default:** `1`
+
diff --git a/docs/pages/docs/introduction.mdx b/docs/pages/docs/introduction.mdx
index 410c3c0a..6728b05b 100644
--- a/docs/pages/docs/introduction.mdx
+++ b/docs/pages/docs/introduction.mdx
@@ -1,6 +1,57 @@
-# Why Effect AI [A brief reasoning on why we built the Effect AI SDK]
+# Why Effect AI
+
+## Introduction
+
+Effect AI was built to solve the problem of AI development.
+It's a platform that connects AI developers with AI trainers to create data to train AI models.
## Problems
-TODO::write problem statement
+The problem that Effect AI is trying to solve is the lack of training data for AI models.
+AI models are only as good as the data they are trained on.
+The more data you have, the better the model will be.
+
+There is an issue though that now we are facing, AI models are being trained with data that ws created by other AI models.
+This is a problem because the data is not always accurate, the model will learn and exacerbate mistakes from the training data.
+Leading to unreliable AI models.
+
+## Solution
+
+The soluiton is to create high quality training data.
+This is where Effect AI comes in.
+
+Effect AI is a platform that connects AI developers with AI trainers.
+
+AI developers can post tasks that they need training data for.
+Ai trainers can then complete these tasks and get paid for their work.
+
+---
+
+
+In the rapidly evolving landscape of artificial intelligence (AI), the quest for optimal development and performance remains paramount.
+Effect AI emerges as a beacon in this realm, offering a solution to a pressing dilemma: the scarcity of high-quality training data for AI models.
+
+At its core, Effect AI addresses the fundamental challenge of AI development—the insufficiency of robust datasets.
+As the adage goes, "garbage in, garbage out"; AI models are only as proficient as the data they are trained upon.
+Consequently, the quest for superior data becomes imperative for fostering the accuracy and reliability of AI technologies.
+
+However, a disconcerting trend has emerged in the AI domain—models being trained with data generated by other AI models.
+This recursive process, while ostensibly efficient, precipitates a cascade of issues.
+Foremost among these concerns is the potential for error amplification: inaccuracies within the training data can be perpetuated and magnified within the AI model, resulting in flawed outcomes and unreliable performance.
+
+Enter Effect AI, a platform meticulously crafted to address these challenges.
+Serving as a nexus between AI developers and AI trainers, Effect AI facilitates the creation of high-quality training data, thereby fortifying the foundations of AI development.
+
+The modus operandi of Effect AI is elegantly simple yet profoundly impactful.
+AI developers leverage the platform to articulate their data requirements through the formulation of tasks.
+These tasks, ranging from image classification to natural language processing, encapsulate the diverse array of challenges encountered in AI development.
+
+In response, AI trainers—individuals proficient in data annotation and curation—step forward to fulfill these tasks.
+Through their collective efforts, they meticulously craft datasets that adhere to the highest standards of quality and accuracy.
+In doing so, they contribute not merely to the advancement of AI technologies but to the cultivation of a virtuous cycle wherein superior data begets superior models.
+
+Crucially, Effect AI's platform incentivizes participation and excellence.
+AI trainers are duly compensated for their contributions, thereby fostering a symbiotic relationship wherein both developers and trainers are mutually benefited.
+In conclusion, Effect AI stands as a testament to the power of innovation in addressing complex challenges.
+By championing the creation of high-quality training data, it not only mitigates the pitfalls of current AI development practices but also charts a course towards a future where AI technologies are synonymous with reliability, accuracy, and societal benefit.
diff --git a/docs/pages/docs/local-development.mdx b/docs/pages/docs/localDevelopment.mdx
similarity index 100%
rename from docs/pages/docs/local-development.mdx
rename to docs/pages/docs/localDevelopment.mdx
diff --git a/docs/pages/index.mdx b/docs/pages/index.mdx
index 4638f512..27cc5b06 100644
--- a/docs/pages/index.mdx
+++ b/docs/pages/index.mdx
@@ -19,7 +19,7 @@ import { HomePage } from 'vocs/components'
Effect AI is the data network for training next-gen transparent AI models. Integrate the SDK into your app and tap into a global, decentralized workforce.
- Get started
+ Get started
GitHub
diff --git a/docs/sidebar.ts b/docs/sidebar.ts
index 639f9b98..29bfeadf 100644
--- a/docs/sidebar.ts
+++ b/docs/sidebar.ts
@@ -1,31 +1,48 @@
import type { Sidebar } from "vocs";
export const sidebar = {
- "/docs/": [
- {
- text: "Introduction",
- items: [
- { text: "Why Effect AI", link: "/docs/introduction" },
- { text: "Getting Started", link: "/docs/getting-started" },
- { text: "Local Development", link: "/docs/local-development" },
- ],
- },
- {
- text: "Guides",
- items: [
- {
- text: "Create Your First Campaign",
- link: "/docs/guides/create-a-campaign",
- },
- ],
- },
- {
- text: "Token",
- items: [{ text: "getPrice", link: "/docs/token/getPrice" }],
- },
- {
- text: "Tasks",
- items: [{ text: "getCampaigns", link: "/docs/tasks/getCampaigns" }],
- },
- ],
+ "/docs/": [
+ {
+ text: "Introduction",
+ items: [
+ { text: "Why Effect AI", link: "/docs/introduction" },
+ { text: "Getting Started", link: "/docs/gettingStarted" },
+ { text: "Local Development", link: "/docs/localDevelopment" },
+ ],
+ },
+ {
+ text: "Guides",
+ items: [
+ {
+ text: "Create Your First Campaign",
+ link: "/docs/guides/createACampaign",
+ },
+ ],
+ },
+ {
+ text: "Token",
+ items: [{ text: "getPrice", link: "/docs/token/getPrice" }],
+ },
+ {
+ text: "Tasks",
+ items: [{ text: "getCampaigns", link: "/docs/tasks/campaigns/getCampaigns" }],
+ },
+ {
+ text: "Local Development",
+ items: [{ text: "localDevelopment", link: "/docs/localDevelopment" }],
+ },
+ {
+ text: "Glossary",
+ items: [
+ { text: "Client Options", link: "/docs/glossary/clientOptions" }
+ ]
+ },
+ {
+ text: "FAQ",
+ items: [
+ { text: "FAQ", link: "/docs/faq" }
+ ]
+ }
+
+ ],
} as const satisfies Sidebar;
diff --git a/package.json b/package.json
index 97c8eee5..aa337f10 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,10 @@
"types": "./dist/exports/index.d.ts",
"files": ["dist"],
"scripts": {
- "test": "vitest run",
+ "test": "bun --env-file=.env test",
+ "test:watch": "bun --env-file=.env test --watch",
+ "test:coverage": "bun --env-file=.env test --coverage",
+ "test:mainnet": "bun --env-file=.env.mainnet test",
"dev": "tsc -w",
"build": "tsc --module es2020",
"lint": "bun biome lint --apply .",
@@ -43,12 +46,9 @@
"@biomejs/biome": "1.7.0",
"@changesets/changelog-github": "^0.4.5",
"@changesets/cli": "^2.23.2",
- "@greymass/abi2core": "^2.0.1",
"@size-limit/preset-big-lib": "^11.1.2",
- "@types/dotenv": "^8.2.0",
- "dotenv": "^16.3.1",
- "typescript": "^5.4.3",
- "vitest": "^1.4.0"
+ "@greymass/abi2core": "^2.0.1",
+ "typescript": "^5.4.3"
},
"dependencies": {
"@wharfkit/antelope": "^1.0.7",
diff --git a/src/actions/atomic/getAccountAssets.test.ts b/src/actions/atomic/getAccountAssets.test.ts
new file mode 100644
index 00000000..5de25b0e
--- /dev/null
+++ b/src/actions/atomic/getAccountAssets.test.ts
@@ -0,0 +1,48 @@
+import { test, expect, describe } from "bun:test";
+import { getAccountAssets, type IAssetRow } from "./getAccountAssets";
+import { destructureEnv, testClientSession } from "../../testHelper";
+import { Name } from "@wharfkit/antelope";
+import { createClient, eos, jungle4 } from "../../exports";
+
+describe("getAccountAssets", async () => {
+ const accountAssetExample: IAssetRow = {
+ asset_id: "2199024546793",
+ collection_name: "pomelo",
+ schema_name: "astronauts",
+ template_id: 7022,
+ ram_payer: "pomelo",
+ backed_tokens: [],
+ immutable_serialized_data: [
+ 5, 46, 81, 109, 90, 57, 102, 105, 106, 75, 54, 100, 109, 113, 66, 121, 66,
+ 81, 117, 105, 57, 66, 71, 74, 111, 68, 77, 83, 122, 84, 76, 90, 104, 70,
+ 55, 69, 70, 53, 90, 88, 78, 112, 57, 67, 111, 78, 102, 71, 9, 6, 67, 121,
+ 98, 111, 114, 103, 10, 8, 65, 113, 117, 97, 114, 105, 117, 109, 11, 5, 67,
+ 108, 111, 119, 110, 12, 6, 77, 111, 100, 101, 114, 110, 13, 4, 78, 111,
+ 110, 101, 14, 4, 66, 97, 108, 100, 15, 4, 78, 111, 110, 101, 16, 16, 68,
+ 97, 114, 107, 32, 66, 114, 97, 115, 115, 32, 83, 116, 117, 100, 115, 17,
+ 11, 70, 108, 111, 119, 101, 114, 32, 69, 121, 101, 115, 18, 6, 78, 111,
+ 114, 109, 97, 108, 19, 4, 78, 111, 110, 101, 20, 4, 78, 111, 110, 101,
+ ],
+ mutable_serialized_data: [],
+ };
+
+ test("getAccountAssets() returns IAssetRow", async () => {
+ const account = Name.from("cryptonode42");
+ const client = await createClient({ network: eos });
+ const assets = await getAccountAssets({ client, account });
+
+ expect(assets).toBeDefined();
+ expect(assets).toBeArray();
+ expect(assets[0]).toMatchObject(accountAssetExample);
+ });
+
+ test("getAccountAssets() should throw return empty array when no assets are found", async () => {
+ const account = Name.from("cryptonode99");
+ const client = await createClient({ network: eos });
+ const assets = await getAccountAssets({ client, account });
+
+ expect(assets).toBeDefined();
+ expect(assets).toBeArray();
+ expect(assets).toBeEmpty();
+ });
+});
diff --git a/src/actions/atomic/getAccountAssets.ts b/src/actions/atomic/getAccountAssets.ts
index 5e2c9e4b..6f0fab18 100644
--- a/src/actions/atomic/getAccountAssets.ts
+++ b/src/actions/atomic/getAccountAssets.ts
@@ -7,7 +7,8 @@ export type getAccountAssetsArgs = {
account: Name;
};
-//Override this interface, atomicassets exports a broken type.
+// TODO: What does this comment mean?
+// Override this interface, atomicassets exports a broken type.
export interface IAssetRow {
asset_id: string;
collection_name: string;
@@ -15,11 +16,11 @@ export interface IAssetRow {
template_id: number;
ram_payer: string;
backed_tokens: string[];
- immutable_serialized_data: Uint8Array;
- mutable_serialized_data: Uint8Array;
+ immutable_serialized_data: number[];
+ mutable_serialized_data: number[];
}
-//TODO:: Implement Pagination
+// TODO:: Implement Pagination
export const getAccountAssets = async ({
client,
account,
diff --git a/src/actions/session/createSession.ts b/src/actions/session/createSession.ts
index 167a3aa8..aa3cc88c 100644
--- a/src/actions/session/createSession.ts
+++ b/src/actions/session/createSession.ts
@@ -17,17 +17,12 @@ export const createSession = async ({
privateKey,
}: CreateSessionArgs) => {
const walletPlugin = new WalletPluginPrivateKey(privateKey);
-
- const { eosRpcUrl, eosChainId } = client.network;
-
+ const { eosRpcUrl: url, eosChainId: id } = client.network;
const session = new Session({
actor,
permission,
- chain: {
- id: eosChainId,
- url: eosRpcUrl,
- },
walletPlugin,
+ chain: { id, url },
});
await setSession({ client, session });
diff --git a/src/actions/tasks/campaigns/campaign.test.ts b/src/actions/tasks/campaigns/campaign.test.ts
new file mode 100644
index 00000000..f42256b5
--- /dev/null
+++ b/src/actions/tasks/campaigns/campaign.test.ts
@@ -0,0 +1,146 @@
+import { describe, test, expect } from "bun:test";
+import { getCampaignById } from "./getCampaignById";
+import { getAllCampaigns } from "./getAllCampaigns";
+import { createCampaign } from "./createCampaign";
+import { getCampaigns, type CampaignWithInfo } from "./getCampaigns";
+import { createClient } from "../../../client";
+import { testClientSession } from "../../../testHelper";
+import { jungle4 } from "../../../exports";
+import type { Mkcampaign } from "../../../@generated/types/effecttasks2";
+
+describe("getCamapignById", async () => {
+ test("getCampaignById() should throw an error when accessing unretrievable id", async () => {
+ const client = createClient({ network: jungle4 });
+ const id = 11111;
+ expect(async () => {
+ await getCampaignById({ client, id });
+ }).toThrowError();
+ });
+
+ test("getCampaignById() should retrieve campaign on testnet", async () => {
+ const client = createClient({ network: jungle4 });
+ const id = 1;
+ const campaign = await getCampaignById({ client, id });
+ expect(campaign).toBeDefined();
+ expect(campaign).toBeObject();
+ expect(campaign).toContainKeys(Object.keys(campaignExample));
+ });
+});
+
+describe("getAllCampaigns", async () => {
+ test("getAllCampaigns() should retrieve all campaign", async () => {
+ const client = createClient({ network: jungle4 });
+ const campaigns = await getAllCampaigns({ client });
+ expect(campaigns).toBeDefined();
+ expect(campaigns).toBeArray();
+ expect(campaigns[0]).toContainKeys(Object.keys(campaignExample));
+ });
+});
+
+describe("getCampaigns", async () => {
+ test("getCampaigns() should return 3 campaigns", async () => {
+ const client = await testClientSession({ testEnvNetwork: jungle4 });
+ const campaigns = await getCampaigns({ client, limit: 3 });
+ expect(campaigns).toBeDefined();
+ expect(campaigns.rows).toBeArray();
+ expect(campaigns.rows.length).toBe(3);
+ });
+});
+
+describe("createCampaign", async () => {
+ const campaign: Mkcampaign = {
+ owner: ["name", "efxefxefxefx"],
+ content: {
+ field_0: 0,
+ field_1: "QmVKwq3bYM6cPW6kstpiq4WYckWRtdfJnzAmms2iMyGqQg",
+ },
+ max_task_time: 100,
+ reward: { quantity: "42.1234, EFX", contract: "efxtoken1112" },
+ qualis: [],
+ payer: "efxefxefxefx",
+ };
+
+ const data = {
+ version: 1.1,
+ title: "Labelstudio OCR (LAION)",
+ description:
+ "You are contributing to a dataset for conversational style chatbots.",
+ instructions: "Some instructions here",
+ template: "Template here
",
+ input_schema: null,
+ output_schema: null,
+ image: "",
+ category: "",
+ example_task: "",
+ estimated_time: 10,
+ };
+
+ test("createCampaign() should throw an error", async () => {
+ const client = createClient({ network: jungle4 });
+ expect(async () => {
+ await createCampaign({ client, campaign, data });
+ }).toThrowError();
+ });
+
+ test.skip("createCampaign() should create a new campaign", async () => {
+ const client = await testClientSession({ testEnvNetwork: jungle4 });
+ const result = await createCampaign({ client, campaign, data });
+ expect(result).toBeDefined();
+ /**
+ * response: {
+ * transaction_id: "9d321af28b7354c5cbee6ee956ea3e6590228b48539a9f0cafc6a8ca5ffe0ca2",
+ * processed: {
+ * id: "9d321af28b7354c5cbee6ee956ea3e6590228b48539a9f0cafc6a8ca5ffe0ca2",
+ * block_num: 137520447,
+ * block_time: "2024-05-01T03:55:31.500",
+ * producer_block_id: null,
+ * receipt: [Object ...],
+ * elapsed: 4854,
+ * net_usage: 176,
+ * scheduled: false,
+ * action_traces: [
+ * [Object ...]
+ * ],
+ * account_ram_delta: null,
+ * except: null,
+ * error_code: null,
+ * },
+ * }
+ */
+ });
+});
+
+const campaignExample: CampaignWithInfo = {
+ id: 1,
+ reservations_done: 1,
+ total_submissions: 2,
+ total_tasks: 1,
+ active_batch: 1,
+ num_batches: 1,
+ owner: ["name", "efxefxefxefx"],
+ paused: false,
+ content: {
+ field_0: 0,
+ field_1: "QmVKwq3bYM6cPW6kstpiq4WYckWRtdfJnzAmms2iMyGqQg",
+ },
+ max_task_time: 3600,
+ reward: {
+ quantity: "0.0100 EFX",
+ contract: "efxtoken1112",
+ },
+ qualis: [],
+ info: {
+ version: 1.1,
+ title: "Labelstudio OCR (LAION)",
+ description:
+ "You are contributing to a dataset for conversational style chatbots.",
+ instructions: "Some instructions here",
+ template: "Template here",
+ input_schema: null,
+ output_schema: null,
+ image: "",
+ category: "",
+ example_task: "",
+ estimated_time: 10,
+ },
+};
diff --git a/src/actions/tasks/campaigns/getCampaignById.ts b/src/actions/tasks/campaigns/getCampaignById.ts
index 173249b7..3c384709 100644
--- a/src/actions/tasks/campaigns/getCampaignById.ts
+++ b/src/actions/tasks/campaigns/getCampaignById.ts
@@ -1,4 +1,3 @@
-import type { Campaign } from "../../../@generated/types/effecttasks2";
import {
type CampaignWithInfo,
getIpfsResource,
@@ -27,11 +26,8 @@ export const getCampaignById = async ({
});
const [campaign] = response.rows;
-
- campaign.info = await getIpfsResource({
- client,
- hash: campaign.content.field_1,
- });
+ const { field_1: hash } = campaign.content;
+ campaign.info = await getIpfsResource({ client, hash });
return campaign;
};
diff --git a/src/actions/token/getBalance.ts b/src/actions/token/getBalance.ts
index 17766213..38dafafe 100644
--- a/src/actions/token/getBalance.ts
+++ b/src/actions/token/getBalance.ts
@@ -1,4 +1,4 @@
-import type { Name } from "@wharfkit/session";
+import type { Asset, Name } from "@wharfkit/session";
import type { Client } from "../../exports";
export type GetBalanceArgs = {
@@ -6,7 +6,23 @@ export type GetBalanceArgs = {
actor: Name;
};
-export const getBalance = async ({ client, actor }: GetBalanceArgs) => {
+/**
+ * Get the balance of a user
+ * @param {{ client: Client, actor: Name}} Effect SDK client and the actor to get the balance for.
+ * @returns {Promise} The balance of the user
+ * @throws {Error} if no balance is found
+ *
+ * @example
+ * const client = createClient({ network: eos });
+ * const actor = Name.from("cryptonode42");
+ * const balance = await getBalance({ client, actor });
+ * console.log(balance.toString());
+ * // => "100.0000 EFX"
+ */
+export const getBalance = async ({
+ client,
+ actor,
+}: GetBalanceArgs): Promise => {
const { network, provider } = client;
const { contracts } = network.config.efx;
diff --git a/src/actions/token/getDefiBoxPair.ts b/src/actions/token/getDefiBoxPair.ts
index af0b5c8d..55a1b0c6 100644
--- a/src/actions/token/getDefiBoxPair.ts
+++ b/src/actions/token/getDefiBoxPair.ts
@@ -5,7 +5,9 @@ export enum DefiBoxPairEnum {
export const getDefiBoxPair = async (pairEnum: DefiBoxPairEnum) => {
try {
- const result = await window.fetch(
+ // TODO: Check how resilient this is, otherwise figure out how to use FetchProvider from the SDK Client.
+ const useFetch = fetch ?? window.fetch;
+ const result = await useFetch(
"https://eos.greymass.com/v1/chain/get_table_rows",
{
method: "POST",
diff --git a/src/actions/token/getPrice.ts b/src/actions/token/getPrice.ts
index e990aa78..634189f2 100644
--- a/src/actions/token/getPrice.ts
+++ b/src/actions/token/getPrice.ts
@@ -1,5 +1,9 @@
import { DefiBoxPairEnum, getDefiBoxPair } from "./getDefiBoxPair";
+/**
+ * Get the current price of EFX in USDT
+ * @returns {Promis} The current price of EFX in USDT
+ */
export const getPrice = async (): Promise => {
try {
const eosEfxPair = await getDefiBoxPair(DefiBoxPairEnum.EosEfx);
@@ -8,7 +12,6 @@ export const getPrice = async (): Promise => {
Number(eosEfxPair.price1_last) * Number(eosUsdtPair.price0_last);
return efxUsdt;
} catch (error) {
- console.error(error);
throw new Error("Error retrieving EFX Ticker Price from DefiBox");
}
};
diff --git a/src/actions/token/swap.ts b/src/actions/token/swap.ts
index a83e4398..c45ecc53 100644
--- a/src/actions/token/swap.ts
+++ b/src/actions/token/swap.ts
@@ -1,4 +1,9 @@
-import { type AnyAction, Asset, type Name } from "@wharfkit/antelope";
+import {
+ type AnyAction,
+ Asset,
+ type Name,
+ type PermissionLevelType,
+} from "@wharfkit/antelope";
import type { Client } from "../../client";
import { DefiBoxPairEnum } from "./getDefiBoxPair";
import { getPrice } from "./getPrice";
@@ -12,7 +17,7 @@ export const swapDirection = {
export const buildSwapAction = (
direction: string,
actor: Name,
- authorization: { permission: Name; actor: Name }[],
+ authorization: PermissionLevelType[],
amount: number,
tokenContract: string,
efxPrice: number,
@@ -57,18 +62,20 @@ export const buildSwapAction = (
return swapAction[direction];
};
-export const swap = async (
- client: Client,
- amount: number,
- direction: string,
-) => {
+export type SwapArgs = {
+ client: Client;
+ amount: number;
+ direction: "EfxToUsdt" | "UsdtToEfx";
+};
+
+export const swap = async ({ client, amount, direction }: SwapArgs) => {
try {
if (!client.session) {
throw new Error("Session is required for this method.");
}
const { transact, actor, authorization } = client.session;
- const { token } = useEFXContracts(client);
+ const { token: tokenContract } = useEFXContracts(client);
const efxPrice = await getPrice();
const action = buildSwapAction(
@@ -76,7 +83,7 @@ export const swap = async (
actor,
authorization,
amount,
- token,
+ tokenContract,
efxPrice,
);
@@ -86,7 +93,6 @@ export const swap = async (
return await transact({ action });
} catch (e) {
- console.error(e);
- throw new Error("Error swapping tokens");
+ throw new Error(`Error swapping: ${e}`);
}
};
diff --git a/src/actions/token/token.test.ts b/src/actions/token/token.test.ts
new file mode 100644
index 00000000..c700d104
--- /dev/null
+++ b/src/actions/token/token.test.ts
@@ -0,0 +1,64 @@
+import { describe, test, expect } from "bun:test";
+import { getPrice } from "./getPrice";
+import { getBalance } from "./getBalance";
+import { swap, type SwapArgs } from "./swap";
+import { createClient } from "../../client";
+import { testClientSession } from "../../testHelper";
+import { eos, jungle4 } from "../../exports";
+import { Name } from "@wharfkit/antelope";
+
+describe("getPrice", async () => {
+ test("getPrice() should retrieve price on mainnet", async () => {
+ const client = createClient({ network: eos });
+ const price = await getPrice();
+ expect(price).toBeDefined();
+ expect(price).toBeNumber();
+ });
+});
+
+describe("getBalance", async () => {
+ test("getBalance() should retrieve balance from user on mainnet", async () => {
+ const client = createClient({ network: jungle4 });
+ const actor = Name.from("forcedev1234");
+ const balance = await getBalance({ client, actor });
+ expect(balance).toBeDefined();
+ expect(balance.toString()).toBeDefined();
+ expect(balance.toString()).toContain("EFX");
+ });
+
+ test("getBalance() should throw Error retrieving balance from unknown user.", async () => {
+ const client = createClient({ network: eos });
+ const actor = Name.from("cryptonode99");
+ expect(async () => await getBalance({ client, actor })).toThrowError();
+ });
+});
+
+describe("buildSwapAction", async () => {
+ test.todo("buildSwapAction() should return a swap action object.");
+});
+
+describe("Swap", async () => {
+ // Use Mainnet
+
+ test("swap() should throw an error when Session is not set on Client.", async () => {
+ const swapArgs: SwapArgs = {
+ client: createClient({ network: jungle4 }),
+ amount: 1,
+ direction: "UsdtToEfx",
+ };
+
+ expect(async () => await swap(swapArgs)).toThrow(
+ new Error("Error swapping: Error: Session is required for this method."),
+ );
+ });
+
+ test("swap() should fail when amount is 0", async () => {
+ const swapArgs: SwapArgs = {
+ client: await testClientSession({ testEnvNetwork: jungle4 }),
+ amount: 0,
+ direction: "UsdtToEfx",
+ };
+
+ expect(async () => await swap(swapArgs)).toThrow();
+ });
+});
diff --git a/src/actions/vaccount/createAccount.test.ts b/src/actions/vaccount/createAccount.test.ts
new file mode 100644
index 00000000..ab956b37
--- /dev/null
+++ b/src/actions/vaccount/createAccount.test.ts
@@ -0,0 +1,25 @@
+import { expect, test, describe, mock } from "bun:test";
+import { destructureEnv, testClientSession } from "../../testHelper";
+import { createClient } from "../../client";
+import { createVAccount } from "./createAccount";
+import { Name } from "@wharfkit/antelope";
+import { jungle4 } from "../../exports";
+
+describe("Create Virtual account", () => {
+ const testEnvNetwork = jungle4;
+
+ test.skip("createVAccount() should return a TransactResult", async () => {
+ const client = await testClientSession({ testEnvNetwork });
+ const account = Name.from("efxforce1112");
+ const result = await createVAccount({ client, account });
+ expect(result).toBeDefined();
+ });
+
+ test("createVAccount() should throw Error when no Session is found", async () => {
+ expect(async () => {
+ const client = createClient({ network: testEnvNetwork });
+ const account = Name.from("efxforce1112");
+ await createVAccount({ client, account });
+ }).toThrowError();
+ });
+});
diff --git a/src/actions/vaccount/createAccount.ts b/src/actions/vaccount/createAccount.ts
index a53eff86..d9a172d2 100644
--- a/src/actions/vaccount/createAccount.ts
+++ b/src/actions/vaccount/createAccount.ts
@@ -1,4 +1,4 @@
-import { Name, type Session } from "@wharfkit/session";
+import type { Name, TransactResult, Session } from "@wharfkit/session";
import type { Client } from "../../client";
import { ExtendedSymbol } from "../../utils/structs";
import { VAddress } from "../../utils/variants";
@@ -9,11 +9,35 @@ export type CreateVAccountArgs = {
account?: Name;
};
+/**
+ * Creates a virtual Effect account
+ * Client must be initialized
+ * Session is optional if the client has a session already
+ * Account is optional, can be initialized with:
+ *
+ * ```ts
+ * import { Name } from "@wharfkit/session";
+ * const account: Name = Name.from("accountname");
+ * ```
+ * The account name must be a valid EOS account name.
+ * If no account is provided, the current session actor will be used.
+ *
+ * @param {CreateVAccountArgs} { client, session, account } - Provide the client, session, and account name.
+ * @returns {TransactResult = The result of the the transaction.}
+ *
+ * @example
+ * ```ts
+ * import { createVAccount } from "@effectai/effect-js";
+ * import { Name } from "@wharfkit/session";
+ * const account: Name = Name.from("accountname");
+ * const result = await createVAccount({ client, account });
+ * ```
+ */
export const createVAccount = async ({
client,
session,
account,
-}: CreateVAccountArgs) => {
+}: CreateVAccountArgs): Promise => {
const sessionToUse = session ?? client.session;
if (!sessionToUse) {
@@ -21,10 +45,10 @@ export const createVAccount = async ({
}
// If no account is provided, use the current session actor
- const acc = account ?? sessionToUse.actor;
+ const acc: Name = account ?? sessionToUse.actor;
const { actor } = sessionToUse;
- const { contracts } = client.network.config.efx;
+ const { contracts, token } = client.network.config.efx;
const authorization = [
{
@@ -38,8 +62,11 @@ export const createVAccount = async ({
name: "open",
authorization,
data: {
- acc: VAddress.from(Name.from(acc.toString())),
- symbol: new ExtendedSymbol("4,EFX", contracts.token),
+ acc: VAddress.from(acc),
+ symbol: new ExtendedSymbol(
+ `${token.precision},${token.symbol}`,
+ contracts.token,
+ ),
payer: actor,
},
};
diff --git a/src/actions/vaccount/deposit.test.ts b/src/actions/vaccount/deposit.test.ts
new file mode 100644
index 00000000..42c380de
--- /dev/null
+++ b/src/actions/vaccount/deposit.test.ts
@@ -0,0 +1,23 @@
+import { describe, test, expect } from "bun:test";
+import { deposit } from "./deposit";
+import { destructureEnv, testClientSession } from "../../testHelper";
+import { eos, jungle4 } from "../../exports";
+import { getOrCreateVAccount } from "./getOrCreate";
+import { Name } from "@wharfkit/antelope";
+
+describe("deposit", async () => {
+ test.todo("Should throw an error when Session is not set on Client.", () => {
+ // TODO: implement test
+ });
+
+ test.skip("Check that deposit is functioning correctly", async () => {
+ const { network, actor } = destructureEnv(jungle4);
+ const client = await testClientSession({ testEnvNetwork: network });
+ console.debug(client.network);
+ const acc = Name.from(actor);
+ const vAccount = await getOrCreateVAccount({ client, actor: acc });
+ const vAccId = Number(vAccount.id);
+ const result = await deposit({ client, vAccountId: vAccId, amount: 0.1 });
+ console.debug(result);
+ });
+});
diff --git a/src/actions/vaccount/getAccounts.test.ts b/src/actions/vaccount/getAccounts.test.ts
new file mode 100644
index 00000000..7c3505ec
--- /dev/null
+++ b/src/actions/vaccount/getAccounts.test.ts
@@ -0,0 +1,45 @@
+import { expect, test, describe, beforeAll } from "bun:test";
+import type { VAccount } from "../../types/user";
+import { testClientSession } from "../../testHelper";
+import type { Client } from "../../client";
+import { getVAccounts, getAccountById } from "./getAccounts";
+import { Name } from "@wharfkit/antelope";
+import { jungle4 } from "../../exports";
+
+describe("Get Virtual Accounts", () => {
+ const vaccExample: VAccount = {
+ id: 0,
+ nonce: 53,
+ address: ["name", "efxforceacc1"],
+ balance: {
+ quantity: "3525.0000 EFX",
+ contract: "effecttokens",
+ },
+ };
+
+ let client: Client;
+ beforeAll(async () => {
+ client = await testClientSession({ testEnvNetwork: jungle4 });
+ });
+
+ test("getVAccounts() on testnet", async () => {
+ const actor = Name.from("efxforce1112");
+ const vaccs = await getVAccounts({ client, actor });
+ expect(vaccs).toBeDefined();
+ expect(vaccs).toBeArray();
+ expect(vaccs[0]).toContainKeys(Object.keys(vaccExample));
+ });
+
+ test("getAccountById()", async () => {
+ const vacc = await getAccountById({ client, accountId: 0 });
+ expect(vacc).toBeDefined();
+ expect(vacc).toContainKeys(Object.keys(vaccExample));
+ });
+
+ test("getAccountByID() should throw Error", async () => {
+ const accountId = 9999999; // Should be imposible to find
+ expect(async () => {
+ await getAccountById({ client, accountId });
+ }).toThrowError();
+ });
+});
diff --git a/src/actions/vaccount/getAccounts.ts b/src/actions/vaccount/getAccounts.ts
index eb87ebea..381ed7c1 100644
--- a/src/actions/vaccount/getAccounts.ts
+++ b/src/actions/vaccount/getAccounts.ts
@@ -9,6 +9,11 @@ export type GetVAccountsArgs = {
actor: Name;
};
+/**
+ * Get all virtual accounts for a given account
+ * @param {GetVAccountsArgs} getVAccountargs - Object with sdk client and account name
+ * @returns {Promise} VAccount[] - Array of Effect Virtual Accounts
+ */
export const getVAccounts = async ({
client,
actor,
diff --git a/src/actions/vaccount/getAvatar.ts b/src/actions/vaccount/getAvatar.ts
index b1e49ebf..c7cda088 100644
--- a/src/actions/vaccount/getAvatar.ts
+++ b/src/actions/vaccount/getAvatar.ts
@@ -11,7 +11,6 @@ export const getAvatar = async (client: Client, account: string) => {
account,
assetId: daoAvatar.asset_id,
});
- console.debug(asset);
return {
...asset,
img: asset.immutable_deserialized_data?.img ?? defaultImg,
diff --git a/src/client.test.ts b/src/client.test.ts
new file mode 100644
index 00000000..5c6ff416
--- /dev/null
+++ b/src/client.test.ts
@@ -0,0 +1,111 @@
+import { expect, test, describe, beforeAll } from "bun:test";
+import { jungle4, eos } from "../src/exports";
+import { testClientSession, destructureEnv } from "./testHelper";
+import { createClient, Client as ClientConstructor } from "./client";
+import { Name } from "@wharfkit/antelope";
+import { WalletPluginPrivateKey } from "@wharfkit/wallet-plugin-privatekey";
+import { Session } from "@wharfkit/session";
+import { EffectSession } from "./session";
+
+describe("Client", async () => {
+ test("createClient TestNet", () => {
+ const client = createClient({ network: jungle4 });
+ expect(client).toBeDefined();
+ expect(client).toBeInstanceOf(ClientConstructor);
+ });
+
+ test("createClient Mainnet", () => {
+ const client = createClient({ network: eos });
+ expect(client).toBeDefined();
+ expect(client).toBeInstanceOf(ClientConstructor);
+ });
+
+ test("Connect Client to Session Mainnet", async () => {
+ const { network, permission, actor, privateKey } = destructureEnv(eos);
+ const client = createClient({ network });
+
+ expect(client.session).toBeNull();
+
+ // Create wallet with privatekey
+ const walletPlugin = new WalletPluginPrivateKey(privateKey);
+
+ // Set up session with wallet
+ const session = new Session({
+ actor,
+ permission,
+ walletPlugin,
+ chain: {
+ id: network.eosChainId,
+ url: network.eosRpcUrl,
+ },
+ });
+
+ // connect session to client
+ await client.setSession(session);
+
+ expect(client.session).toBeDefined();
+ expect(client.session).toBeInstanceOf(EffectSession);
+ expect(client.session?.vAccount?.address[1]).toEqual(actor);
+ });
+
+ test("Connect Client to Session Testnet", async () => {
+ const { network, permission, actor, privateKey } = destructureEnv(jungle4);
+ const client = createClient({ network });
+
+ expect(client.session).toBeNull();
+
+ // Create wallet with privatekey
+ const walletPlugin = new WalletPluginPrivateKey(privateKey);
+
+ // Set up session with wallet
+ const session = new Session({
+ actor,
+ permission,
+ walletPlugin,
+ chain: {
+ id: network.eosChainId,
+ url: network.eosRpcUrl,
+ },
+ });
+
+ // connect session to client
+ await client.setSession(session);
+
+ expect(client.session).toBeDefined();
+ expect(client.session).toBeInstanceOf(EffectSession);
+ expect(client.session?.vAccount?.address[1]).toEqual(actor);
+ });
+});
+
+describe("Client testHelper Mainnet", async () => {
+ const testEnvNetwork = eos;
+
+ test("testClient defined Mainnet", async () => {
+ const client = await testClientSession({ testEnvNetwork });
+ expect(client).toBeDefined();
+ expect(client).toBeInstanceOf(ClientConstructor);
+ });
+
+ test("testClient session connected Mainnet", async () => {
+ const client = await testClientSession({ testEnvNetwork });
+ expect(client.session).toBeDefined();
+ expect(client.session?.vAccount).toBeDefined();
+ expect(client.session?.actor).toBeInstanceOf(Name);
+ });
+});
+
+describe("Client testHelper Testnet", async () => {
+ const testEnvNetwork = jungle4;
+ test("testClient defined testnet", async () => {
+ const client = await testClientSession({ testEnvNetwork });
+ expect(client).toBeDefined();
+ expect(client).toBeInstanceOf(ClientConstructor);
+ });
+
+ test("testClient session connected Testnet", async () => {
+ const client = await testClientSession({ testEnvNetwork });
+ expect(client.session).toBeDefined();
+ expect(client.session?.vAccount).toBeDefined();
+ expect(client.session?.actor).toBeInstanceOf(Name);
+ });
+});
diff --git a/src/client.ts b/src/client.ts
index 4635bf57..a66acbe2 100644
--- a/src/client.ts
+++ b/src/client.ts
@@ -4,14 +4,11 @@ import {
type FetchProviderOptions,
} from "@wharfkit/antelope";
import type { Session } from "@wharfkit/session";
-
+import type { Network } from "./types/network";
import { type Cache, MemoryCache } from "./cache";
-
+import { type CacheManager, IDBCache, createCacheManager } from "./cache";
import { getOrCreateVAccount } from "./actions/vaccount/getOrCreate";
import { EffectSession } from "./session";
-import type { Network } from "./types/network";
-import { type CacheManager, IDBCache, createCacheManager } from "./cache";
-import { chains } from "./constants/network";
export interface ClientOpts {
ipfsCacheDurationInMs?: number | null;
@@ -23,6 +20,14 @@ const defaultClientOpts: ClientOpts = {
ipfsCacheDurationInMs: 600_000, // 10 minutes
};
+/**
+ * Represents a client for the Effect SDK, used for interacting with the network.
+ * @class Client
+ * @property {FetchProvider} fetchProvider - the fetch provider for the client, allows for custtom fetch implementations.
+ * @property {Network} network - the network configuration for the client.
+ * @property {ClientOpts} options = additonal options for the client.
+ * @property {APIClient} provider - the API client for the client.
+ */
export class Client {
public readonly fetchProvider: FetchProvider;
public readonly network: Network;
@@ -37,6 +42,11 @@ export class Client {
return this._session;
}
+ /**
+ * Constructs a new instance of the Client class.
+ * @param {Network} network The network configuration for the client.
+ * @param {ClientOpts} options Additional options for the client.
+ */
constructor(network: Network, options: ClientOpts) {
this.options = { ...defaultClientOpts, ...options };
@@ -57,7 +67,42 @@ export class Client {
}
}
- // Set the session for the client
+ /**
+ * Sets the session for the client, using user credentials.
+ * @param {Session|null} session The session to set for the client.
+ * @returns {Promise | null} The updated session for the client.
+ * @throws {Error} If failed to set session.
+ *
+ * ```typescript
+ * // Import the Effect Client, network configuration, and sdk function
+ * import { createClient, eos, createBatch } from "@effectai/effect-js"
+ * import { Session } from "@wharfkit/session";
+ * import { WalletPluginPrivateKey } from "@wharfkit/wallet-plugin-privatekey";
+ *
+ * // Create a new client
+ * const client = createClient({ network: eos });
+ *
+ * // Set up wallet with privatekey
+ * const walletPlugin = new WalletPluginPrivateKey(privateKey);
+ *
+ * // Set up session with wallet and chain
+ * const session = new Session({
+ * actor,
+ * permission,
+ * walletPlugin,
+ * chain: {
+ * id: network.eosChainId,
+ * url: network.eosRpcUrl,
+ * },
+ * });
+ *
+ * // Connect session to client
+ * await client.setSession(session);
+ *
+ * // Use the client to create a batch with the session
+ * await createBatch({ client, batch, data })
+ * ```
+ */
public setSession = async (session: Session | null) => {
try {
if (!session) {
@@ -83,13 +128,35 @@ export class Client {
};
}
+/**
+ * Create a new client for the Effect SDK.
+ * Pass in the network configuration and any additonal options.
+ * Client must be used as an argument to SDK functions to interact with the network.
+ *
+ * @param {Object} createClientParam - The network and options for the Effect SDK client.
+ * @param {Network} createClientParam.network - The network configuration for the client.
+ * @param {ClientOpts} [createClientParam.options] - Additional options for the client.
+ * @returns {Client} A new instance of the Client class.
+ *
+ * ```typescript
+ * // Import the Effect Client, network configuration, and sdk function
+ * import { createClient, eos, getAccountById } from "@effectai/effect-js"
+ *
+ * // Create a new client
+ * const client = createClient({ network: eos });
+ *
+ * // Use the client to get an account by id
+ * const accountId = 42;
+ * const vAccount = await getAccountById({ client, accountId });
+ * ```
+ */
export const createClient = ({
network,
session,
options = {},
}: {
network: Network;
- session: Session;
+ session?: Session;
options?: ClientOpts;
}) => {
return new Client(network, options);
diff --git a/src/constants/config.ts b/src/constants/config.ts
index 885e9f52..e310ac90 100644
--- a/src/constants/config.ts
+++ b/src/constants/config.ts
@@ -5,7 +5,7 @@ import type {
RelayerConfig,
} from "../types/network";
-export const efxConfig: EfxConfig = {
+export const efxTestnetConfig: EfxConfig = {
token: {
symbol: "EFX",
precision: 4,
@@ -21,6 +21,22 @@ export const efxConfig: EfxConfig = {
},
};
+export const efxMainnetConfig: EfxConfig = {
+ token: {
+ symbol: "EFX",
+ precision: 4,
+ },
+ contracts: {
+ tasks: "force.efx",
+ token: "effecttokens",
+ stake: "efxstakepool",
+ feepool: "feepool.efx",
+ proposals: "daoproposals",
+ vaccount: "vaccount.efx",
+ dao: "theeffectdao",
+ },
+};
+
export const ipfsConfig: IpfsConfig = {
ipfsEndpoint: "https://ipfs.effect.ai",
};
@@ -38,9 +54,9 @@ export const relayerConfig: RelayerConfig = {
"https://vaccount-relayer-service-jungle-96xyn.ondigitalocean.app",
};
-export const defaultNetworkConfig = {
- efx: efxConfig,
+export const defaultNetworkConfig = (network: string) => ({
+ efx: network === "eos" ? efxMainnetConfig : efxTestnetConfig,
ipfs: ipfsConfig,
atomic: atomicConfig,
relayer: relayerConfig,
-};
+});
diff --git a/src/constants/network.ts b/src/constants/network.ts
index 30a27607..e4d4da30 100644
--- a/src/constants/network.ts
+++ b/src/constants/network.ts
@@ -8,7 +8,7 @@ export const jungle4: Network = {
eosChainId:
"73e4385a2708e6d7048834fbc1079f2fabb17b3c125b146af438971e90716c4d",
- config: defaultNetworkConfig,
+ config: defaultNetworkConfig("jungle4"),
};
export const eos: Network = {
@@ -17,8 +17,7 @@ export const eos: Network = {
eosRpcUrl: "https://eos.greymass.com/",
eosChainId:
"73e4385a2708e6d7048834fbc1079f2fabb17b3c125b146af438971e90716c4d",
-
- config: defaultNetworkConfig,
+ config: defaultNetworkConfig("eos"),
};
export const chains = [eos, jungle4];
diff --git a/src/testHelper.ts b/src/testHelper.ts
new file mode 100644
index 00000000..6ae93aff
--- /dev/null
+++ b/src/testHelper.ts
@@ -0,0 +1,87 @@
+import {
+ PrivateKey,
+ type PrivateKeyType,
+ Session,
+ PermissionLevel,
+} from "@wharfkit/session";
+import { WalletPluginPrivateKey } from "@wharfkit/wallet-plugin-privatekey";
+import { jungle4, eos } from "../src/exports";
+import { createClient } from "./../src/client";
+import type { Client } from "./../src/client";
+import type { Network } from "./types/network";
+
+declare module "bun" {
+ interface Env {
+ TESTNET_PERMISSION: string;
+ TESTNET_NETWORK_NAME: string;
+ TESTNET_ACTOR: string;
+ TESTNET_PRIVATE_KEY: string;
+ MAINNET_PERMISSION: string;
+ MAINNET_NETWORK_NAME: string;
+ MAINNET_ACTOR: string;
+ MAINNET_PRIVATE_KEY: string;
+ }
+}
+
+export interface testEnv {
+ network: Network;
+ networkName: string;
+ permission: string;
+ actor: string;
+ privateKey: PrivateKeyType;
+}
+
+export const destructureEnv = (networkEnv: Network) => {
+ // Mainnet Config
+ if (networkEnv === eos) {
+ return {
+ network: eos,
+ networkName: process.env.MAINNET_NETWORK_NAME,
+ permission: process.env.MAINNET_PERMISSION,
+ actor: process.env.MAINNET_ACTOR,
+ privateKey: PrivateKey.from(process.env.MAINNET_PRIVATE_KEY),
+ };
+ }
+
+ // Testnet Config
+ return {
+ network: jungle4,
+ networkName: process.env.TESTNET_NETWORK_NAME,
+ permission: process.env.TESTNET_PERMISSION,
+ actor: process.env.TESTNET_ACTOR,
+ privateKey: PrivateKey.from(process.env.TESTNET_PRIVATE_KEY),
+ };
+};
+
+export const testClientSession = async ({
+ testEnvNetwork,
+}: { testEnvNetwork: Network }): Promise => {
+ // Retrieve parameters for session.
+ const { network, permission, actor, privateKey } =
+ destructureEnv(testEnvNetwork);
+ const { eosRpcUrl: url, eosChainId: id } = network;
+
+ // Create client
+ const client = createClient({ network });
+
+ // Set up wallet with privatekey
+ const pk: PrivateKeyType = PrivateKey.fromString(
+ privateKey.toString(),
+ false,
+ );
+ const walletPlugin = new WalletPluginPrivateKey(pk);
+
+ // Set up session with wallet and chain
+ const session = new Session({
+ actor,
+ permission,
+ walletPlugin,
+ chain: { id, url },
+ permissionLevel: PermissionLevel.from(`${actor}@${permission}`),
+ });
+
+ // Connect session to client
+ await client.setSession(session);
+
+ return client;
+};
diff --git a/test/helpers.ts b/test/helpers.ts
deleted file mode 100644
index 9238fa2c..00000000
--- a/test/helpers.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Session } from "@wharfkit/session";
-import { WalletPluginPrivateKey } from "@wharfkit/wallet-plugin-privatekey";
-import { jungle4 } from "../src/constants/network";
-import { createClient } from "./../src/client";
-
-const client = createClient(jungle4, {});
-
-//jungle 4 test key
-const walletPlugin = new WalletPluginPrivateKey(
- "5KSG1pLHubiQ2JD4G4Pr32zxz7oQvpagBofYPrdS1FALKVjxdPM",
-);
-
-//set a test session to jungle
-client.state.setState({
- session: new Session({
- actor: "forcedev1234",
- permission: "active",
- chain: {
- id: "73e4385a2708e6d7048834fbc1079f2fabb17b3c125b146af438971e90716c4d",
- url: "https://jungle4.greymass.com",
- },
- walletPlugin,
- }),
-});
-
-export { client };
diff --git a/tsconfig.json b/tsconfig.json
index 0f9c3aeb..250d1a38 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -15,6 +15,6 @@
"outDir": "dist",
"skipLibCheck": true
},
- "exclude": ["node_modules/**/*"],
+ "exclude": ["node_modules/**/*", "**/*.test.ts"],
"include": ["src/**/*", "abis/**/*"]
}