Skip to content

Conversation

@Yolley
Copy link
Collaborator

@Yolley Yolley commented Dec 15, 2025

  • feat: support protocol level airdrop fees;
    • for v0 airdrops claim fees will be applied by the sdk;
    • for v1 airdrops claim fees are applied by the protocl when calling claimV2;
    • all other fees are applied by the protocol always, check getFees and getDefaultFees methods to fetch fees beforehand:
    // fetch default fees applied if wallet has no partner fees
    const defaultFees = await client.getDefaultFees();
    // fetch fees for a given wallet
    const partnerFees = await client.getFees(wallet.publicKey);
  • feat!: fully migrate to runtime generated anchor client in distributor;
    BREAKING CHANGE: sdk no longer exports distributor related classes, they are only exported as types, i.e.:
    import type { MerkleDistributor, ClaimStatus } from "@streamflow/distributor/solana";
  • feat: add partner oracle to common as it's reused across multiple protocols;
  • chore: bump anchor to v0.32.1 (no breaking changes for our package);

BREAKING CHANGE: when the protocol is deployed, newDistributor will work only from this SDK version, since it has 2 new accounts added.

@github-actions
Copy link

github-actions bot commented Dec 15, 2025

Alpha versions of packages are published 📦

PR: #334
Version: 11.0.0-alpha.p334.30f5e22
NPM: https://www.npmjs.com/package/@streamflow/common/v/11.0.0-alpha.p334.30f5e22

"packages": [
"packages/*"
],
"version": "11.0.0",
Copy link
Contributor

Choose a reason for hiding this comment

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

lfg 🔥

Copy link
Contributor

Choose a reason for hiding this comment

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

this number is going up faster than the scale in my room during a week at an all inclusive in Turkey

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🌚

/**
* Build a partner oracle program without a wallet, just to fetch accounts.
*/
export function buildPartnerOracle(connection: Connection): Program<PartnerOracle> {
Copy link
Contributor

Choose a reason for hiding this comment

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

are we not gonna expose anything else for fee fetching for use in the App? e.g. like we do for vesting

Copy link
Collaborator Author

@Yolley Yolley Dec 15, 2025

Choose a reason for hiding this comment

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

We do expose
image

Copy link
Collaborator Author

@Yolley Yolley Dec 15, 2025

Choose a reason for hiding this comment

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

And in the common package I just expose the program since it's shared across protocols.

"packages": [
"packages/*"
],
"version": "11.0.0",
Copy link
Contributor

Choose a reason for hiding this comment

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

this number is going up faster than the scale in my room during a week at an all inclusive in Turkey

const feeConfig = await this.getFeeConfig();
const nowTs = new BN(Math.trunc(Date.now() / 1000));
return (
feeConfig.partners.filter((partner) => partner.pubkey.equals(pubkey) && partner.expiryTs.gt(nowTs))[0] ?? null
Copy link
Contributor

Choose a reason for hiding this comment

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

should it throw if 1+ partners are found? just as a sanity check so we don't forget to clean some stale stuff

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 should never happen as the protocol does not allow it when writing fees, but I'll add a throw to mark it as unexpected behavior then.

Comment on lines 30 to 42
export type MerkleDistributor = MerkleDistributorAccountTypes["merkleDistributor"];

type IdlInstruction<IDL extends Idl, Name extends IDL["instructions"][number]["name"]> = Extract<
IDL["instructions"][number],
{ name: Name }
>;

type AccountsOfMethod<M extends keyof Program<MerkleDistributorIDL>["methods"]> = Parameters<
ReturnType<Program<MerkleDistributorIDL>["methods"][M]>["accounts"]
>[0];
type ArgsOfMethod<M extends keyof Program<MerkleDistributorIDL>["methods"]> = Parameters<
Program<MerkleDistributorIDL>["methods"][M]
>;
Copy link
Contributor

Choose a reason for hiding this comment

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

a minor, but would be cool to have it globally like in common/anchor for instance since we're increasing anchor inference usage everywhere. Might be useful 🤷‍♂️ but not forcing it.
Just don't want it to be MIA later if a need appear again

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Makes sense, tho I put it to common/solana/types for now - as solana package is not exactly a web3js wrapper, it just contains common solana utilities.

Comment on lines +51 to +54
// const [feeOracle] = PublicKey.findProgramAddressSync(
// [Buffer.from(b"airdrop_config")],
// new PublicKey(PARTNER_ORACLE_PROGRAM_ID[cluster]),
// );
Copy link
Contributor

Choose a reason for hiding this comment

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

outdated?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Just keeping how the address was derived as it's a PDA>

Comment on lines +47 to +48
[ICluster.Testnet]: "pardoTarcc6HKsPcbXkVycxsJsoN9QEzrdHgVdHAGY3",
[ICluster.Local]: "pardoTarcc6HKsPcbXkVycxsJsoN9QEzrdHgVdHAGY3",
Copy link
Contributor

Choose a reason for hiding this comment

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

Curious do we ever use these two? in all other places as well

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Nop, never, prob makes sense to remove the unused clusters are at some point lol, may be in v23

const { distributor, mint, clawbackReceiver, tokenProgram, tokenVault, admin } = accounts;
const { mint, clawbackReceiver, tokenProgram, admin } = accounts;
const distributorKey = getDistributorPda(this.merkleDistributorProgram.programId, pk(accounts.mint), data.version);
const tokenVaultKey = getAssociatedTokenAddressSync(pk(mint), pk(admin!), true, pk(tokenProgram));
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it anchor type inference that is broken and requires to enforce non nullish type or the NewDistributorAccounts helper?

Is it even allowed to call the getNewDistributorInstruction function w/o an admin account?

Suggested change
const tokenVaultKey = getAssociatedTokenAddressSync(pk(mint), pk(admin!), true, pk(tokenProgram));
const tokenVaultKey = getAssociatedTokenAddressSync(pk(mint), pk(admin), true, pk(tokenProgram));

Copy link
Contributor

Choose a reason for hiding this comment

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

the line below has it too

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So, admin is a signer which can be inferred from a transaction by anchor ts - that's why it's an optional account. In our case we always set it tho as we pass signer to all instruction related methods.

Makes sense to wrap it in Required then I guess since admin is the only optional account in this structure.

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 make types as close to the real life as possible - it is one of ways to make sdks "easier to use for outsiders" (and for us actually too)

…helpers to common, throw on found partners > 1
@Yolley Yolley merged commit 7184b12 into master Jan 14, 2026
15 checks passed
@Yolley Yolley deleted the feat/distributor_protocol_fees branch January 14, 2026 12:02
@github-actions
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants