Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
129 changes: 129 additions & 0 deletions cli/commands/tributary/create.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import BN from "bn.js";
import { PublicKey } from "@solana/web3.js";
import {
getTributarySdk,
sendTributaryInstructions,
} from "../../utils/tributary/sdk.js";
import {
USDC_MINT,
DEFAULT_GATEWAY,
FREQUENCY_MAP,
USDC_DECIMALS,
} from "../../utils/tributary/constants.js";
import { encodeMemo } from "../../utils/tributary/format.js";
import { resolveWallet } from "../../utils/wallet/resolve.js";
import { print, printError } from "../../utils/common/output.js";
import { requireAgentToken } from "../../utils/trading/guards.js";

export default async function subscribeCreate(args, flags) {
const [amountStr, recipientStr] = args;
const interval = flags.interval;

if (!amountStr || !recipientStr || !interval) {
printError(
"missing_args",
"Usage: zerion subscribe create <amount> <recipient> --interval <frequency>",
{
example: "zerion subscribe create 10 2Nsnn... --interval monthly",
},
);
process.exit(1);
}

const amount = parseFloat(amountStr);
if (Number.isNaN(amount) || amount <= 0) {
printError(
"invalid_amount",
`Amount must be a positive number, got "${amountStr}"`,
);
process.exit(1);
}

const frequency = FREQUENCY_MAP[interval];
if (!frequency) {
printError("invalid_interval", `Unknown interval "${interval}"`, {
suggestion: `Valid: ${Object.keys(FREQUENCY_MAP).join(", ")}`,
});
process.exit(1);
}

const { walletName, address } = resolveWallet({
...flags,
chain: "solana",
});

const tokenMint = flags.token || USDC_MINT;
const gateway = flags.gateway || DEFAULT_GATEWAY;
const autoRenew = !flags["no-auto-renew"];
const maxRenewals = flags["max-renewals"]
? parseInt(flags["max-renewals"])
: null;
const memo = encodeMemo(flags.memo, 64);

const amountSmallest = new BN(
Math.round(amount * Math.pow(10, USDC_DECIMALS)),
);

const passphrase = await requireAgentToken(
"for Tributary subscription",
walletName,
);

const sdk = await getTributarySdk(address);

try {
const instructions = await sdk.createSubscription(
new PublicKey(tokenMint),
new PublicKey(recipientStr),
new PublicKey(gateway),
amountSmallest,
autoRenew,
maxRenewals,
frequency,
memo,
);

if (flags["dry-run"]) {
print({
dryRun: true,
instructionCount: instructions.length,
amount: amountStr,
recipient: recipientStr,
interval,
token: tokenMint,
gateway,
});
return;
}

const txResult = await sendTributaryInstructions(
instructions,
address,
walletName,
passphrase,
);

print({
subscription: {
amount: amountSmallest.toString(),
amountHuman: `${amount} USDC`,
recipient: recipientStr,
gateway,
token: tokenMint,
interval,
autoRenew,
maxRenewals,
},
transaction: {
hash: txResult.hash,
status: txResult.status,
},
});
} catch (err) {
printError(
err.code || "subscription_error",
`Failed to create subscription: ${err.message}`,
);
process.exit(1);
}
}
72 changes: 72 additions & 0 deletions cli/commands/tributary/delete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { PublicKey } from "@solana/web3.js";
import {
getTributarySdk,
sendTributaryInstructions,
} from "../../utils/tributary/sdk.js";
import { USDC_MINT } from "../../utils/tributary/constants.js";
import { resolveWallet } from "../../utils/wallet/resolve.js";
import { print, printError } from "../../utils/common/output.js";
import { requireAgentToken } from "../../utils/trading/guards.js";

export default async function subscribeDelete(args, flags) {
const [policyIdStr] = args;

if (!policyIdStr) {
printError("missing_args", "Usage: zerion subscribe delete <policy-id>", {
example: "zerion subscribe delete 1 --token <mint>",
});
process.exit(1);
}

const policyId = parseInt(policyIdStr, 10);
if (Number.isNaN(policyId) || policyId < 1) {
printError(
"invalid_policy_id",
`Policy ID must be a positive integer, got "${policyIdStr}"`,
);
process.exit(1);
}

const { walletName, address } = resolveWallet({
...flags,
chain: "solana",
});

const tokenMint = flags.token || USDC_MINT;
const passphrase = await requireAgentToken(
"for Tributary subscription",
walletName,
);

try {
const sdk = await getTributarySdk(address);

const instructions = await sdk.deletePaymentPolicy(
new PublicKey(tokenMint),
policyId,
);

const txResult = await sendTributaryInstructions(
instructions,
address,
walletName,
passphrase,
);

print({
action: "delete",
policyId,
token: tokenMint,
transaction: {
hash: txResult.hash,
status: txResult.status,
},
});
} catch (err) {
printError(
err.code || "delete_error",
`Failed to delete policy ${policyId}: ${err.message}`,
);
process.exit(1);
}
}
85 changes: 85 additions & 0 deletions cli/commands/tributary/list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { PublicKey } from "@solana/web3.js";
import { getTributarySdk } from "../../utils/tributary/sdk.js";
import { USDC_MINT, USDC_DECIMALS } from "../../utils/tributary/constants.js";
import { resolveWallet } from "../../utils/wallet/resolve.js";
import { print, printError } from "../../utils/common/output.js";
import {
decodePolicyType,
decodeStatus,
decodeMemo,
formatAmount,
formatPrettyTable,
} from "../../utils/tributary/format.js";
import { isPrettyMode } from "../../utils/common/output.js";

export default async function subscribeList(args, flags) {
const { walletName, address } = resolveWallet({
...flags,
chain: "solana",
});

const statusFilter = flags.status || "all";
const tokenMints = [USDC_MINT];
const policies = [];

try {
const sdk = await getTributarySdk(address);

for (const mint of tokenMints) {
const userPaymentPda = sdk.getUserPaymentPda(
new PublicKey(address),
new PublicKey(mint),
);
const userPolicies = await sdk.getPaymentPoliciesByUserPayment(
userPaymentPda.address,
);

for (const p of userPolicies) {
const status = decodeStatus(p.account.status);
if (statusFilter !== "all" && status !== statusFilter) continue;
policies.push(p);
}
}

if (flags.pretty || isPrettyMode()) {
process.stdout.write(formatPrettyTable(address, policies));
} else {
print({
wallet: address,
count: policies.length,
policies: policies.map((p) => {
const decoded = decodePolicyType(p.account.policyType);
const result = {
policyId: p.account.policyId,
policyPda: p.publicKey.toString(),
...decoded,
status: decodeStatus(p.account.status),
recipient: p.account.recipient.toString(),
gateway: p.account.gateway.toString(),
};
if (decoded.type === "subscription") {
result.amount = `${formatAmount(decoded.amount, USDC_DECIMALS)} USDC`;
result.nextPaymentDue = decoded.nextPaymentDue
? new Date(decoded.nextPaymentDue * 1000).toISOString()
: null;
}
const memo = decodeMemo(p.account.memo);
if (memo) result.memo = memo;
if (p.account.createdAt) {
result.createdAt = new Date(
p.account.createdAt.toNumber() * 1000,
).toISOString();
}
return result;
}),
});
}
} catch (err) {
console.trace(err)
printError(
err.code || "list_error",
`Failed to list subscriptions: ${err.message}`,
);
process.exit(1);
}
}
73 changes: 73 additions & 0 deletions cli/commands/tributary/pause.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { PublicKey } from "@solana/web3.js";
import {
getTributarySdk,
sendTributaryInstructions,
} from "../../utils/tributary/sdk.js";
import { USDC_MINT } from "../../utils/tributary/constants.js";
import { resolveWallet } from "../../utils/wallet/resolve.js";
import { print, printError } from "../../utils/common/output.js";
import { requireAgentToken } from "../../utils/trading/guards.js";

export default async function subscribePause(args, flags) {
const [policyIdStr] = args;

if (!policyIdStr) {
printError("missing_args", "Usage: zerion subscribe pause <policy-id>", {
example: "zerion subscribe pause 1 --token <mint>",
});
process.exit(1);
}

const policyId = parseInt(policyIdStr, 10);
if (Number.isNaN(policyId) || policyId < 1) {
printError(
"invalid_policy_id",
`Policy ID must be a positive integer, got "${policyIdStr}"`,
);
process.exit(1);
}

const { walletName, address } = resolveWallet({
...flags,
chain: "solana",
});

const tokenMint = flags.token || USDC_MINT;
const passphrase = await requireAgentToken(
"for Tributary subscription",
walletName,
);

try {
const sdk = await getTributarySdk(address);

const instructions = await sdk.changePaymentPolicyStatus(
new PublicKey(tokenMint),
policyId,
{ paused: {} },
);

const txResult = await sendTributaryInstructions(
instructions,
address,
walletName,
passphrase,
);

print({
action: "pause",
policyId,
token: tokenMint,
transaction: {
hash: txResult.hash,
status: txResult.status,
},
});
} catch (err) {
printError(
err.code || "pause_error",
`Failed to pause policy ${policyId}: ${err.message}`,
);
process.exit(1);
}
}
Loading