Skip to content

Commit

Permalink
Add fees retrieval (#114)
Browse files Browse the repository at this point in the history
* add fees retrieval

* rename fields for consistency
  • Loading branch information
Yolley authored Dec 12, 2023
1 parent c28cc07 commit ecf966f
Show file tree
Hide file tree
Showing 16 changed files with 266 additions and 5 deletions.
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 { streamflowFee: Number(value) / 100, partnerFee: 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 %
*/
public getFees(getFeesData: IGetFeesData): Promise<IFees | null> {
return this.nativeStreamClient.getFees(getFeesData);
}

/**
* Returns default Streamflow Fee in %
*/
public getDefaultStreamflowFee(): Promise<number> {
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 {
streamflowFee: number;
partnerFee: 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 {
streamflowFee: fees.streamflow_fee.toNumber() / 100,
partnerFee: fees.partner_fee.toNumber() / 100,
};
}

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

/**
* 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 {
streamflowFee: Number(filteredPartners[0].strm_fee.toFixed(4)),
partnerFee: 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

0 comments on commit ecf966f

Please sign in to comment.