Skip to content

Commit b8822d8

Browse files
authored
feat: support price based update (#315)
1 parent 80e9d8c commit b8822d8

File tree

10 files changed

+250
-51
lines changed

10 files changed

+250
-51
lines changed

lerna.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"packages": [
33
"packages/*"
44
],
5-
"version": "9.0.4",
5+
"version": "9.1.0",
66
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
77
"command": {
88
"run": {

packages/common/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@streamflow/common",
3-
"version": "9.0.4",
3+
"version": "9.1.0",
44
"description": "Common utilities and types used by streamflow packages.",
55
"homepage": "https://github.com/streamflow-finance/js-sdk/",
66
"type": "module",

packages/distributor/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@streamflow/distributor",
3-
"version": "9.0.4",
3+
"version": "9.1.0",
44
"description": "JavaScript SDK to interact with Streamflow Airdrop protocol.",
55
"homepage": "https://github.com/streamflow-finance/js-sdk/",
66
"main": "./dist/cjs/index.cjs",

packages/eslint-config/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@streamflow/eslint-config",
3-
"version": "9.0.4",
3+
"version": "9.1.0",
44
"description": "ESLint configuration for Streamflow protocol.",
55
"homepage": "https://github.com/streamflow-finance/js-sdk/",
66
"engines": {

packages/launchpad/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@streamflow/launchpad",
3-
"version": "9.0.4",
3+
"version": "9.1.0",
44
"description": "JavaScript SDK to interact with Streamflow Launchpad protocol.",
55
"homepage": "https://github.com/streamflow-finance/js-sdk/",
66
"main": "./dist/cjs/index.cjs",

packages/staking/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@streamflow/staking",
3-
"version": "9.0.4",
3+
"version": "9.1.0",
44
"description": "JavaScript SDK to interact with Streamflow Staking protocol.",
55
"homepage": "https://github.com/streamflow-finance/js-sdk/",
66
"main": "./dist/cjs/index.cjs",

packages/stream/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@streamflow/stream",
3-
"version": "9.0.4",
3+
"version": "9.1.0",
44
"description": "JavaScript SDK to interact with Streamflow protocol.",
55
"homepage": "https://github.com/streamflow-finance/js-sdk/",
66
"main": "./dist/cjs/index.cjs",

packages/stream/solana/StreamClient.ts

Lines changed: 41 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,10 @@ export class SolanaStreamClient extends BaseStreamClient {
408408
} = streamParams;
409409
const { isNative, sender, computeLimit, computePrice, metadataPubKeys } = extParams;
410410

411-
invariant(priceOracle || !oracleType || oracleType === "none", "Price oracle is required for the specified oracle type" );
411+
invariant(
412+
priceOracle || !oracleType || oracleType === "none",
413+
"Price oracle is required for the specified oracle type",
414+
);
412415
assertHasPublicKey(sender, "Sender's PublicKey is not available, check passed wallet adapter!");
413416

414417
const recipientPublicKey = new PublicKey(recipient);
@@ -441,10 +444,10 @@ export class SolanaStreamClient extends BaseStreamClient {
441444
// We already have getCreateATAInstructions for WSOL and sender address
442445
// This check avoids sending double create ATA instructions which would cause transaction to fail
443446
if (recipientPublicKey.toString() !== sender.toString() || !isNative) {
444-
ixs.push(
445-
...(await this.getCreateATAInstructions([recipientPublicKey], mintPublicKey, sender, true, tokenProgramId)),
446-
);
447-
}
447+
ixs.push(
448+
...(await this.getCreateATAInstructions([recipientPublicKey], mintPublicKey, sender, true, tokenProgramId)),
449+
);
450+
}
448451

449452
const encodedUIntArray = new TextEncoder().encode(streamName);
450453
const streamNameArray = Array.from(encodedUIntArray);
@@ -533,7 +536,6 @@ export class SolanaStreamClient extends BaseStreamClient {
533536
}: ICreateStreamData,
534537
{ sender, metadataPubKeys, isNative = false, computePrice, computeLimit }: IPrepareCreateStreamSolanaExt,
535538
): Promise<ICreateStreamInstructions> {
536-
537539
assertHasPublicKey(sender, "Sender's PublicKey is not available, check passed wallet adapter!");
538540

539541
const ixs: TransactionInstruction[] = prepareBaseInstructions(this.connection, {
@@ -622,10 +624,7 @@ export class SolanaStreamClient extends BaseStreamClient {
622624
* @returns Create result
623625
*/
624626
public async createUnchecked(data: ICreateStreamData, extParams: ICreateStreamSolanaExt): Promise<ICreateResult> {
625-
626-
const { ixs, metadata, metadataPubKey } = await this.prepareCreateUncheckedInstructions(data,
627-
extParams,
628-
);
627+
const { ixs, metadata, metadataPubKey } = await this.prepareCreateUncheckedInstructions(data, extParams);
629628
const { tx, hash, context } = await prepareTransaction(
630629
this.connection,
631630
ixs,
@@ -820,20 +819,11 @@ export class SolanaStreamClient extends BaseStreamClient {
820819
});
821820
}
822821

823-
const prepareInstructions = await this.getCreateATAInstructions(
824-
[partnerPublicKey],
825-
mintPublicKey,
826-
sender,
827-
true,
828-
);
822+
const prepareInstructions = await this.getCreateATAInstructions([partnerPublicKey], mintPublicKey, sender, true);
829823

830824
if (isNative) {
831825
const totalDepositedAmount = recipients.reduce((acc, recipient) => recipient.amount.add(acc), new BN(0));
832-
const nativeInstructions = await prepareWrappedAccount(
833-
this.connection,
834-
sender.publicKey,
835-
totalDepositedAmount,
836-
);
826+
const nativeInstructions = await prepareWrappedAccount(this.connection, sender.publicKey, totalDepositedAmount);
837827
prepareInstructions.push(...nativeInstructions);
838828
}
839829

@@ -1094,8 +1084,7 @@ export class SolanaStreamClient extends BaseStreamClient {
10941084

10951085
assertHasPublicKey(invoker, "Invoker's PublicKey is not available, check passed wallet adapter!");
10961086

1097-
const ixs: TransactionInstruction[] = await this.prepareWithdrawInstructions({ id, amount },
1098-
extParams);
1087+
const ixs: TransactionInstruction[] = await this.prepareWithdrawInstructions({ id, amount }, extParams);
10991088

11001089
const metadata = new PublicKey(id);
11011090
await this.applyCustomAfterInstructions(ixs, customInstructions, metadata);
@@ -1126,7 +1115,6 @@ extParams);
11261115
{ id, amount = WITHDRAW_AVAILABLE_AMOUNT }: IWithdrawData,
11271116
{ invoker, checkTokenAccounts, computePrice, computeLimit }: IPrepareStreamSolanaExt,
11281117
): Promise<TransactionInstruction[]> {
1129-
11301118
assertHasPublicKey(invoker, "Invoker's PublicKey is not available, check passed wallet adapter!");
11311119

11321120
const ixs: TransactionInstruction[] = prepareBaseInstructions(this.connection, {
@@ -1179,7 +1167,6 @@ extParams);
11791167
* @returns Transaction result
11801168
*/
11811169
public async cancel(cancelData: ICancelData, extParams: IInteractStreamSolanaExt): Promise<ITransactionResult> {
1182-
11831170
const ixs = await this.prepareCancelInstructions(cancelData, extParams);
11841171
const { tx, hash, context } = await prepareTransaction(this.connection, ixs, extParams.invoker.publicKey);
11851172
const signature = await signAndExecuteTransaction(
@@ -1232,7 +1219,6 @@ extParams);
12321219
{ id }: ICancelData,
12331220
{ invoker, checkTokenAccounts, computePrice, computeLimit }: IPrepareStreamSolanaExt,
12341221
): Promise<TransactionInstruction[]> {
1235-
12361222
assertHasPublicKey(invoker, "Invoker's PublicKey is not available, check passed wallet adapter!");
12371223

12381224
const streamPublicKey = new PublicKey(id);
@@ -1285,7 +1271,6 @@ extParams);
12851271
{ id }: ICancelData,
12861272
{ invoker, checkTokenAccounts, computePrice, computeLimit }: IPrepareStreamSolanaExt,
12871273
): Promise<TransactionInstruction[]> {
1288-
12891274
assertHasPublicKey(invoker, "Invoker's PublicKey is not available, check passed wallet adapter!");
12901275

12911276
const streamPublicKey = new PublicKey(id);
@@ -1373,7 +1358,6 @@ extParams);
13731358
{ id, newRecipient }: ITransferData,
13741359
{ invoker, computePrice, computeLimit = 100001 }: IPrepareStreamSolanaExt,
13751360
): Promise<TransactionInstruction[]> {
1376-
13771361
assertHasPublicKey(invoker, "Invoker's PublicKey is not available, check passed wallet adapter!");
13781362

13791363
const ixs: TransactionInstruction[] = prepareBaseInstructions(this.connection, {
@@ -1655,7 +1639,7 @@ extParams);
16551639
const alignedProxy = await this.alignedProxyProgram.account.contract.fetch(
16561640
deriveContractPDA(this.alignedProxyProgram.programId, pubkey),
16571641
);
1658-
invariant(alignedProxy, "Couldn't get aligned proxy account info");
1642+
invariant(alignedProxy, "Couldn't get aligned proxy account info");
16591643
return { publicKey: pubkey, account: new AlignedContract(stream, alignedProxy) };
16601644
}
16611645
return { publicKey: pubkey, account: new Contract(stream) };
@@ -1704,25 +1688,41 @@ invariant(alignedProxy, "Couldn't get aligned proxy account info");
17041688
data: IUpdateData,
17051689
{ invoker, computePrice, computeLimit }: IPrepareStreamSolanaExt,
17061690
): Promise<TransactionInstruction[]> {
1707-
17081691
assertHasPublicKey(invoker, "Invoker's PublicKey is not available, check passed wallet adapter!");
17091692

17101693
const streamPublicKey = new PublicKey(data.id);
17111694
const escrow = await this.connection.getAccountInfo(streamPublicKey);
17121695

17131696
invariant(escrow, "Couldn't get account info");
17141697

1698+
const { sender: senderPublicKey } = decodeStream(escrow.data);
1699+
const isAlignedUnlock = this.isAlignedUnlock(streamPublicKey, senderPublicKey);
1700+
1701+
if (isAlignedUnlock && (data.enableAutomaticWithdrawal !== undefined || data.amountPerPeriod !== undefined)) {
1702+
throw new Error("Automatic withdrawal and rate update are not possible in price-based vesting!");
1703+
}
1704+
17151705
const ixs: TransactionInstruction[] = prepareBaseInstructions(this.connection, {
17161706
computePrice,
17171707
computeLimit,
17181708
});
17191709
ixs.push(
1720-
await updateStreamInstruction(data, this.programId, {
1721-
authority: invoker.publicKey,
1722-
metadata: streamPublicKey,
1723-
withdrawor: WITHDRAWOR_PUBLIC_KEY,
1724-
systemProgram: SystemProgram.programId,
1725-
}),
1710+
await (isAlignedUnlock
1711+
? this.alignedProxyProgram.methods
1712+
.updateContract({
1713+
transferableBySender: data.transferableBySender ?? null,
1714+
transferableByRecipient: data.transferableByRecipient ?? null,
1715+
cancelableBySender: data.cancelableBySender ?? null,
1716+
})
1717+
.accounts({ sender: invoker.publicKey, streamMetadata: streamPublicKey, withdrawor: WITHDRAWOR_PUBLIC_KEY })
1718+
.accountsPartial({ streamflowProgram: this.programId })
1719+
.instruction()
1720+
: updateStreamInstruction(data, this.programId, {
1721+
authority: invoker.publicKey,
1722+
metadata: streamPublicKey,
1723+
withdrawor: WITHDRAWOR_PUBLIC_KEY,
1724+
systemProgram: SystemProgram.programId,
1725+
})),
17261726
);
17271727

17281728
return ixs;
@@ -1771,13 +1771,13 @@ invariant(alignedProxy, "Couldn't get aligned proxy account info");
17711771
return extractSolanaErrorCode(err.toString() ?? "Unknown error!", logs);
17721772
}
17731773

1774-
/**
1774+
/**
17751775
* Utility function that checks whether the associated stream address is an aligned unlock contract, indicated by whether the sender/creator is a PDA
17761776
*/
1777-
public isAlignedUnlock(streamPublicKey: PublicKey, senderPublicKey: PublicKey) {
1778-
const pda = deriveContractPDA(this.alignedProxyProgram.programId, streamPublicKey);
1779-
return senderPublicKey.equals(pda);
1780-
}
1777+
public isAlignedUnlock(streamPublicKey: PublicKey, senderPublicKey: PublicKey) {
1778+
const pda = deriveContractPDA(this.alignedProxyProgram.programId, streamPublicKey);
1779+
return senderPublicKey.equals(pda);
1780+
}
17811781

17821782
/**
17831783
* Utility function to generate metadata for a Contract or return existing Pubkey
@@ -1833,4 +1833,3 @@ invariant(alignedProxy, "Couldn't get aligned proxy account info");
18331833
}
18341834
}
18351835
}
1836-

packages/stream/solana/descriptor/idl/streamflow_aligned_unlocks.json

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"address": "aSTRM2NKoKxNnkmLWk9sz3k74gKBk9t7bpPrTGxMszH",
33
"metadata": {
44
"name": "streamflow_aligned_unlocks",
5-
"version": "1.3.0",
5+
"version": "1.4.0",
66
"spec": "0.1.0",
77
"description": "Proxy to update unlock amount within Streamflow vesting protocol according to Token performance and other metrics"
88
},
@@ -876,6 +876,80 @@
876876
],
877877
"args": []
878878
},
879+
{
880+
"name": "update_contract",
881+
"discriminator": [
882+
78,
883+
184,
884+
162,
885+
174,
886+
233,
887+
5,
888+
40,
889+
82
890+
],
891+
"accounts": [
892+
{
893+
"name": "sender",
894+
"writable": true,
895+
"signer": true
896+
},
897+
{
898+
"name": "proxy_metadata",
899+
"docs": [
900+
"Proxy Contract"
901+
],
902+
"writable": true,
903+
"pda": {
904+
"seeds": [
905+
{
906+
"kind": "const",
907+
"value": [
908+
99,
909+
111,
910+
110,
911+
116,
912+
114,
913+
97,
914+
99,
915+
116
916+
]
917+
},
918+
{
919+
"kind": "account",
920+
"path": "stream_metadata"
921+
}
922+
]
923+
}
924+
},
925+
{
926+
"name": "stream_metadata",
927+
"writable": true
928+
},
929+
{
930+
"name": "withdrawor",
931+
"writable": true
932+
},
933+
{
934+
"name": "streamflow_program",
935+
"address": "strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m"
936+
},
937+
{
938+
"name": "system_program",
939+
"address": "11111111111111111111111111111111"
940+
}
941+
],
942+
"args": [
943+
{
944+
"name": "params",
945+
"type": {
946+
"defined": {
947+
"name": "UpdateContractParams"
948+
}
949+
}
950+
}
951+
]
952+
},
879953
{
880954
"name": "update_test_oracle",
881955
"discriminator": [
@@ -1471,6 +1545,32 @@
14711545
]
14721546
}
14731547
},
1548+
{
1549+
"name": "UpdateContractParams",
1550+
"type": {
1551+
"kind": "struct",
1552+
"fields": [
1553+
{
1554+
"name": "transferable_by_sender",
1555+
"type": {
1556+
"option": "bool"
1557+
}
1558+
},
1559+
{
1560+
"name": "transferable_by_recipient",
1561+
"type": {
1562+
"option": "bool"
1563+
}
1564+
},
1565+
{
1566+
"name": "cancelable_by_sender",
1567+
"type": {
1568+
"option": "bool"
1569+
}
1570+
}
1571+
]
1572+
}
1573+
},
14741574
{
14751575
"name": "UpdateTestOracleParams",
14761576
"type": {

0 commit comments

Comments
 (0)