Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fees retrieval #114

Merged
merged 2 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"packages": [
"packages/*"
],
"version": "5.7.1",
"version": "5.8.0",
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
}
8 changes: 7 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 35 additions & 1 deletion packages/stream/aptos/StreamClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import {
ICreateMultipleStreamData,
ICreateResult,
ICreateStreamData,
IGetFeesData,
IGetOneData,
IFees,
IMultiTransactionResult,
IRecipient,
ITopUpData,
Expand All @@ -18,7 +20,14 @@ import {
IWithdrawData,
} from "../common/types";
import { APTOS_PROGRAM_IDS } from "./constants";
import { Contract, ICreateStreamAptosExt, ITransactionAptosExt, StreamResource } from "./types";
import {
ConfigResource,
Contract,
FeeTableResource,
ICreateStreamAptosExt,
ITransactionAptosExt,
StreamResource,
} from "./types";
import { AptosWalletWrapper } from "./wallet";
import { extractAptosErrorCode } from "./utils";

Expand Down Expand Up @@ -237,6 +246,31 @@ export default class AptosStreamClient extends BaseStreamClient {
return { ixs: [payload], txId: hash };
}

public async getFees({ address }: IGetFeesData): Promise<IFees | null> {
const resource = await this.client.getAccountResource(
this.programId,
`${this.programId}::fees::FeeTable`
);
const data = resource.data as unknown as FeeTableResource;
const value = await this.client.getTableItem(data.values.handle, {
key_type: "address",
key: address,
value_type: "u64",
});
if (!value) {
return null;
}
return { streamflow_fee: Number(value) / 100, partner_fee: 0 };
}

public async getDefaultStreamflowFee(): Promise<number> {
const resource = await this.client.getAccountResource(
this.programId,
`${this.programId}::admin::ConfigV2`
);
return Number((resource.data as unknown as ConfigResource).streamflow_fees) / 100;
}

public extractErrorCode(err: Error): string | null {
return extractAptosErrorCode(err.toString() ?? "Unknown error!");
}
Expand Down
14 changes: 14 additions & 0 deletions packages/stream/aptos/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,20 @@ export interface StreamResource {
withdrawn: string;
}

export interface FeeTableResource {
values: {
handle: string;
};
}

export interface ConfigResource {
admin: string;
streamflow_fees: string;
treasury: string;
tx_fee: string;
withdrawor: string;
}

export class Contract implements Stream {
magic: number;

Expand Down
6 changes: 6 additions & 0 deletions packages/stream/common/BaseStreamClient.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {
ICreateStreamData,
ICreateMultipleStreamData,
IFees,
IWithdrawData,
ICancelData,
ITransferData,
ITopUpData,
IGetFeesData,
IGetOneData,
IUpdateData,
ITransactionResult,
Expand Down Expand Up @@ -42,6 +44,10 @@ export abstract class BaseStreamClient {

abstract update(updateData: IUpdateData, chainSpecificParams: any): Promise<ITransactionResult>;

abstract getFees(getFeesData: IGetFeesData, chainSpecificParams: any): Promise<IFees | null>;

abstract getDefaultStreamflowFee(chainSpecificParams: any): Promise<number>;

// eslint-disable-next-line @typescript-eslint/no-unused-vars
extractErrorCode(err: Error): string | null {
return null;
Expand Down
16 changes: 16 additions & 0 deletions packages/stream/common/GenericStreamClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
ICreateResult,
IGetAllData,
Stream,
IFees,
IGetFeesData,
} from "./types";
import { handleContractError } from "./utils";
import { AptosStreamClient, ICreateStreamAptosExt, ITransactionAptosExt } from "../aptos";
Expand Down Expand Up @@ -262,4 +264,18 @@ export default class GenericStreamClient<T extends IChain> extends BaseStreamCli
this.nativeStreamClient.extractErrorCode
);
}

/**
* Returns streamflow and partner fees for the specific wallet in %
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, since it's public SDK, we should capitalize S in Streamflow

*/
public getFees(getFeesData: IGetFeesData): Promise<IFees | null> {
return this.nativeStreamClient.getFees(getFeesData);
}

/**
* Returns default Streamflow Fee in %
*/
public getDefaultStreamflowFee(): Promise<number> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd move all of this out from StreamClient into some Fees file

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's all part of the protocol, like for EVM we interact directly with our program for example. It would be probably better rename streamClient to Protocol Client though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it sounds like a part of bigger rework and I am not sure when we'll get to it (and who will own in).

return this.nativeStreamClient.getDefaultStreamflowFee();
}
}
9 changes: 9 additions & 0 deletions packages/stream/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ export interface IGetOneData {
id: string;
}

export interface IGetFeesData {
address: string;
}

export interface IGetAllData {
address: string;
type?: StreamType;
Expand All @@ -83,6 +87,11 @@ export interface ITransactionResult {
txId: string;
}

export interface IFees {
streamflow_fee: number;
Yolley marked this conversation as resolved.
Show resolved Hide resolved
partner_fee: number;
}

export interface ICreateResult extends ITransactionResult {
metadataId: MetadataId;
}
Expand Down
20 changes: 19 additions & 1 deletion packages/stream/evm/StreamClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import {
ICreateMultipleStreamData,
ICreateResult,
ICreateStreamData,
IGetFeesData,
IGetAllData,
IGetOneData,
IFees,
IMultiTransactionResult,
IRecipient,
ITopUpData,
Expand All @@ -26,7 +28,7 @@ import { BNB_PROGRAM_IDS, ETHEREUM_PROGRAM_IDS, POLYGON_PROGRAM_IDS } from "./co
import abi from "./abi";
import ercAbi from "./ercAbi";
import { BASE_FEE } from "../common/constants";
import { EvmContract, StreamAbiResult } from "./types";
import { EvmContract, FeesAbiResult, StreamAbiResult } from "./types";
import { extractEvmErrorCode } from "./utils";

export default class EvmStreamClient extends BaseStreamClient {
Expand Down Expand Up @@ -251,6 +253,22 @@ export default class EvmStreamClient extends BaseStreamClient {
return extractEvmErrorCode(err.toString() ?? "Unknown error!");
}

public async getFees({ address }: IGetFeesData): Promise<IFees | null> {
const fees: FeesAbiResult = await this.readContract.getFees(address);
if (!fees.exists) {
return null;
}
return {
streamflow_fee: fees.streamflow_fee.toNumber() / 100,
partner_fee: fees.partner_fee.toNumber() / 100,
};
}

public async getDefaultStreamflowFee(): Promise<number> {
const fee = await this.readContract.getStreamflowFees();
return fee.toNumber() / 100;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even better to divide before moving to number :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would need to add a dependency then, because EVM returns BigNumber

}

/**
* Returns StreamClient protocol program ID.
*/
Expand Down
6 changes: 6 additions & 0 deletions packages/stream/evm/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ export interface StreamAbiResult {
withdrawn: BigNumber;
}

export interface FeesAbiResult {
exists: boolean;
streamflow_fee: BigNumber;
partner_fee: BigNumber;
}

export class EvmContract implements Stream {
magic: number;

Expand Down
3 changes: 2 additions & 1 deletion packages/stream/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@streamflow/stream",
"version": "5.7.1",
"version": "5.8.0",
"description": "JavaScript SDK to interact with Streamflow protocol.",
"main": "dist/index.js",
"homepage": "https://github.com/streamflow-finance/js-sdk/",
Expand Down Expand Up @@ -38,6 +38,7 @@
"@suiet/wallet-kit": "0.2.18",
"aptos": "1.4.0",
"bn.js": "5.2.1",
"borsh": "^2.0.0",
"bs58": "5.0.0",
"ethereum-checksum-address": "0.0.8",
"ethers": "5.7.2",
Expand Down
34 changes: 34 additions & 0 deletions packages/stream/solana/StreamClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
sendAndConfirmRawTransaction,
BlockheightBasedTransactionConfirmationStrategy,
} from "@solana/web3.js";
import * as borsh from "borsh";

import {
Account,
Expand Down Expand Up @@ -46,6 +47,10 @@ import {
TX_FINALITY_CONFIRMED,
WITHDRAWOR_PUBLIC_KEY,
FEE_ORACLE_PUBLIC_KEY,
DEFAULT_STREAMFLOW_FEE,
PARTNER_ORACLE_PROGRAM_ID,
FEES_METADATA_SEED,
PARTNERS_SCHEMA,
} from "./constants";
import {
withdrawStreamInstruction,
Expand All @@ -64,7 +69,9 @@ import {
ICreateResult,
ICreateStreamData,
IGetAllData,
IGetFeesData,
IGetOneData,
IFees,
IMultiTransactionResult,
IRecipient,
IStreamConfig,
Expand All @@ -79,6 +86,7 @@ import {
ICreateMultiError,
} from "../common/types";
import { BaseStreamClient } from "../common/BaseStreamClient";
import { IPartnerLayout } from "./instructionTypes";

const METADATA_ACC_SIZE = 1104;

Expand Down Expand Up @@ -801,6 +809,32 @@ export default class SolanaStreamClient extends BaseStreamClient {
};
}

public async getFees({ address }: IGetFeesData): Promise<IFees | null> {
const [metadataPubKey] = PublicKey.findProgramAddressSync(
[Buffer.from(FEES_METADATA_SEED)],
new PublicKey(PARTNER_ORACLE_PROGRAM_ID)
);
const data = await this.connection.getAccountInfo(metadataPubKey);
if (!data) {
return null;
}
const partners = borsh.deserialize(PARTNERS_SCHEMA, data!.data) as unknown as IPartnerLayout[];
const filteredPartners = partners.filter(
(item) => new PublicKey(item.pubkey).toString() === address
);
if (filteredPartners.length === 0) {
return null;
}
return {
streamflow_fee: Number(filteredPartners[0].strm_fee.toFixed(4)),
partner_fee: Number(filteredPartners[0].partner_fee.toFixed(4)),
};
}

public async getDefaultStreamflowFee(): Promise<number> {
return DEFAULT_STREAMFLOW_FEE;
}

public extractErrorCode(err: Error): string | null {
return extractSolanaErrorCode(err.toString() ?? "Unknown error!");
}
Expand Down
11 changes: 11 additions & 0 deletions packages/stream/solana/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export const STREAMFLOW_PROGRAM_ID = "strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5

export const STREAMFLOW_DEVNET_PROGRAM_ID = "FGjLaVo5zLGdzCxMo9gu9tXr1kzTToKd8C8K7YS5hNM1";

export const PARTNER_ORACLE_PROGRAM_ID = "pardpVtPjC8nLj1Dwncew62mUzfChdCX1EaoZe8oCAa";

export const STREAMFLOW_TREASURY_PUBLIC_KEY = new PublicKey(
"5SEpbdjFK5FxwTvfsGMXVQTD2v4M2c5tyRTxhdsPkgDw"
);
Expand All @@ -31,8 +33,17 @@ export const FEE_ORACLE_PUBLIC_KEY = new PublicKey("B743wFVk2pCYhV91cn287e1xY7f1

export const AIRDROP_TEST_TOKEN = "Gssm3vfi8s65R31SBdmQRq6cKeYojGgup7whkw4VCiQj";

export const FEES_METADATA_SEED = Buffer.from("strm_fees");

export const DEFAULT_STREAMFLOW_FEE = 0.99;

export const AIRDROP_AMOUNT = 1; // 1 SOL is the cap on the testnet

export const PARTNER_SCHEMA = {
struct: { pubkey: { array: { type: "u8", len: 32 } }, partner_fee: "f32", strm_fee: "f32" },
};
export const PARTNERS_SCHEMA = { array: { type: PARTNER_SCHEMA } };

export const SOLANA_ERROR_MATCH_REGEX = /custom program error: (0x\d{2})/;

export const SOLANA_ERROR_MAP: { [key: number]: string } = {
Expand Down
6 changes: 6 additions & 0 deletions packages/stream/solana/instructionTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,9 @@ export interface IUpdateStreamLayout {
export interface ITopupStreamLayout {
amount: Uint8Array;
}

export interface IPartnerLayout {
pubkey: Uint8Array;
partner_fee: number;
strm_fee: number;
}
7 changes: 7 additions & 0 deletions packages/stream/solana/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { CREATE_PARAMS_PADDING } from "./constants";
import {
ICreateStreamLayout,
ICreateUncheckedStreamLayout,
IPartnerLayout,
IStreamLayout,
ITopupStreamLayout,
IUpdateStreamLayout,
Expand Down Expand Up @@ -66,6 +67,12 @@ export const streamLayout: BufferLayout.Structure<IStreamLayout> = BufferLayout.
BufferLayout.blob(8, "funds_unlocked_at_last_rate_change"),
]);

export const partnerLayout: BufferLayout.Structure<IPartnerLayout> = BufferLayout.struct([
BufferLayout.blob(32, "pubkey"),
BufferLayout.f32("partner_fee"),
BufferLayout.f32("strm_fee"),
]);

/**
* Create stream instruction layout
*/
Expand Down
Loading