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
1 change: 1 addition & 0 deletions Anchor.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[toolchain]
anchor_version = "0.30.1"

[features]
resolution = true
Expand Down
32 changes: 32 additions & 0 deletions common/addresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export interface DataFeed {

export interface TokenAddresses {
acRole?: PublicKey;
acGlobalOverride?: {
ac: PublicKey;
acRole: PublicKey;
};
mToken?: PublicKey;
tokenAuthority?: {
seed: string;
Expand Down Expand Up @@ -99,6 +103,28 @@ export const addresses: Record<string, NetworkAddresses> = {
account: new PublicKey('DNJMfdgrrVHKp1nFY5Qoqq14erqzdJoMve5THgKpCkrb'),
},
},
[MProduct.PSV]: {
acRole: new PublicKey('77YMLUMHD5Pdq2qvYCjTNL5oP1Z5hKPK4yuak2EaLJya'),
acGlobalOverride: {
ac: new PublicKey('6kGJVtfqxi2Jv5Ejb7W8UwWd9yuhckA69u9zjpRnVQiW'),
acRole: new PublicKey('9LWZYKZdNN6cBFf8Tu2NLUujgqZ2HZQ8ZsYJVUvnuHHr'),
},
mToken: new PublicKey('H4hLpHyvjMiDytckLgAhRyTzHAoSYg2eQ9RGTUissayx'),
tokenAuthority: {
account: new PublicKey('AEk5FmQYH6uqxsiQRX6yUkyugLd9QVheSsP4eKFhZYyK'),
seed: 'psv-token-authority',
},
mTokenDataFeed: new PublicKey('K5CPdTisCUonoqzjJH2NBiHRY7gU7oPxSS5wymmU78z'),
mTokenUnderlyingFeed: new PublicKey('3JQuSWEyd8CwcniXWfSWGgzrUQfcsTsgtGHe92pVaTHi'),
minter: {
commonVault: new PublicKey('GgmNCBisHT3SQ6aVyabPXyJE6ss8v4s23JFnBXJBzasz'),
account: new PublicKey('6FqbTK8xSiQPA5BLyzTkR2hjXPNN1jrut8qU333o8hea'),
},
redeemer: {
commonVault: new PublicKey('93Qpf7sfihJr5a6HdZ5N53jqe7ieevxuEmLqdAVBRrK8'),
account: new PublicKey('DKp86fdtsZMbegJNcxH3ea9eGhahXDWsxaSXCe79MYXZ'),
},
},
},
feeds: {
[PaymentToken.USDC]: {
Expand All @@ -107,6 +133,12 @@ export const addresses: Record<string, NetworkAddresses> = {
dataFeed: new PublicKey('EY9TeqHx3QbKfSbZW7vZPNeg6Y8nwprsa9rm6okGCKpn'),
underlyingFeed: new PublicKey('Dpw1EAVrSB1ibxiDQyTAW6Zip3J4Btk2x4SgApQCeFbX'),
},
[PaymentToken.wSOL]: {
token: new PublicKey('So11111111111111111111111111111111111111112'),
tokenProgram: TOKEN_PROGRAM_ID,
dataFeed: new PublicKey('3XCjjrbWFkiUmUs1i3MKk9GbXSAGQw7vZAhJb6XH3xCH'),
underlyingFeed: new PublicKey('H1kJWEqotQcdg2fiMNby1Fhtp44EZnCLFbuvwk7fmTBy'),
},
},
},
};
22 changes: 22 additions & 0 deletions common/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,28 @@ export interface CustomSignerModule {
string | { sent: boolean; txId?: string; signedTransaction?: string; signature?: string }
>;
getSolanaWalletAddressForAction: (action: string, mtoken?: string, chainId?: string) => string;
createSolanaAddressBookContract: ({
address,
contractName,
mToken,
chain,
contractTag,
}: {
address: string;
contractName: string;
mToken: string;
chain?: string;
contractTag?: string;
}) => Promise<
| {
sent: boolean;
txId?: undefined;
}
| {
sent: boolean;
txId: string;
}
>;
}

function createProvider(network: string, wallet: Wallet): AnchorProvider {
Expand Down
58 changes: 45 additions & 13 deletions common/scriptRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,30 @@ async function loadCustomSignerModule(): Promise<CustomSignerModule> {
return {
signSolanaTransaction: module.signSolanaTransaction,
getSolanaWalletAddressForAction: module.getSolanaWalletAddressForAction,
createSolanaAddressBookContract: module.createSolanaAddressBookContract,
};
}

export const createCustomSignerProvider = async (
network: string,
action?: string,
mtoken?: string,
) => {
const isLocalnet = network.toLowerCase() === 'localnet';
const useFordefi = !isLocalnet && !!action;

const customSignerModule = useFordefi ? await loadCustomSignerModule() : undefined;
initCustomSigner(customSignerModule, network);

const { provider, payer } = await createNetworkProvider(
network,
customSignerModule,
action,
mtoken,
);

return { provider, payer, customSignerModule };
};
/**
* Run a script with the appropriate wallet:
* - localnet: always uses WALLET_PATH keypair
Expand All @@ -41,21 +62,32 @@ export async function executeNetworkScript(
mtoken?: string,
): Promise<void> {
try {
const isLocalnet = network.toLowerCase() === 'localnet';
const useFordefi = !isLocalnet && !!action;

const customSignerModule = useFordefi ? await loadCustomSignerModule() : undefined;
initCustomSigner(customSignerModule, network);

const { provider, payer } = await createNetworkProvider(
network,
customSignerModule,
action,
mtoken,
);

const { provider, payer } = await createCustomSignerProvider(network, action, mtoken);
await scriptFn(provider, payer, network);
} catch (error) {
handleError(error);
}
}

export const createSolanaAddressBookContract = async ({
network,
address,
contractName,
mToken,
contractTag,
}: {
network: string;
address: string;
contractName: string;
mToken: string;
contractTag?: string;
}) => {
const { customSignerModule } = await createCustomSignerProvider(network, 'deployer');
return await customSignerModule.createSolanaAddressBookContract({
address,
contractName,
mToken,
chain: network,
contractTag,
});
};
2 changes: 2 additions & 0 deletions common/tokenTypes.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
export enum MProduct {
MTBILL = 'mTBILL',
MFONE = 'mFONE',
PSV = 'pSV',
}

export enum PaymentToken {
USDC = 'USDC',
USDT = 'USDT',
wSOL = 'wSOL',
}

export function isMProduct(value: string): value is MProduct {
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"deploy:global-ac-role": "tsx scripts/tasks/deploy/network/deploy-global-ac-role.ts",
"deploy:global-ac": "tsx scripts/tasks/deploy/network/deploy-global-ac.ts",
"deploy:token-ac-role": "tsx scripts/tasks/deploy/deploy-token-ac-role.ts",
"deploy:token-ac:global-override": "tsx scripts/tasks/deploy/deploy-global-ac-override.ts",
"deploy:token-ac-role:global-override": "tsx scripts/tasks/deploy/deploy-global-ac-role-override.ts",
"deploy:token-mint": "tsx scripts/tasks/deploy/deploy-token-mint.ts",
"deploy:token-authority": "tsx scripts/tasks/deploy/deploy-token-authority.ts",
"deploy:token-datafeed": "tsx scripts/tasks/deploy/deploy-token-datafeed.ts",
Expand All @@ -31,7 +33,9 @@
"transfer:authority": "tsx scripts/tasks/manage/transfer-authority.ts",
"update:data-feed": "tsx scripts/tasks/manage/update-data-feed.ts",
"update:manual-feed-price": "tsx scripts/tasks/manage/update-manual-feed-price.ts",
"delegate": "tsx scripts/tasks/manage/delegate.ts"
"delegate": "tsx scripts/tasks/manage/delegate.ts",
"pause:functions": "tsx scripts/tasks/manage/pause-functions.ts",
"add:to-addressbook": "tsx scripts/tasks/manage/add-to-addressbook.ts"
},
"dependencies": {
"@coral-xyz/anchor": "0.30.1",
Expand Down
5 changes: 5 additions & 0 deletions scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ Run once per network before deploying any tokens:

### Token Deployment

If you need separated greenlist for a token, first deploy ac global overrides:

1. `yarn deploy:token-ac-role:global-override --mtoken <token> --network <network>`
2. `yarn deploy:token-ac:global-override --mtoken <token> --network <network>`

Run in order for each token:

1. `yarn deploy:token-ac-role --mtoken <token> --network <network>`
Expand Down
2 changes: 1 addition & 1 deletion scripts/configs/roles-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ export const ROLE_GROUPS = {
SOLANA_ROLES.M_MINTER, // Manual mint tokens
SOLANA_ROLES.M_BURNER, // Manual burn tokens
SOLANA_ROLES.M_FREEZER, // Freeze/thaw accounts
SOLANA_ROLES.VAULT_PAUSER, // Pause vault operations
],

VAULTS_MANAGER: [
SOLANA_ROLES.VAULT_ADMIN, // Manage both minter and redeemer vaults
SOLANA_ROLES.VAULT_PAUSER, // Pause vault operations
],

ORACLE_MANAGER: [
Expand Down
2 changes: 2 additions & 0 deletions scripts/configs/tokens/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { TokenConfigWithNetworks } from '@/scripts/configs/types';

import { mFONEConfig } from './mFONE';
import { mTBILLConfig } from './mTBILL';
import { pSVConfig } from './pSV';

export const tokenConfigs: Partial<Record<MProduct, TokenConfigWithNetworks>> = {
[MProduct.MTBILL]: mTBILLConfig,
[MProduct.MFONE]: mFONEConfig,
[MProduct.PSV]: pSVConfig,
};
80 changes: 80 additions & 0 deletions scripts/configs/tokens/pSV.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { PaymentToken } from '@/common/tokenTypes';
import { TokenConfigWithNetworks } from '@/scripts/configs/types';
import { UNLIMITED } from '@/scripts/constants/pricing';
import { VaultActionIds } from '@/test/constants/vaults.constants';

export const pSVConfig: TokenConfigWithNetworks = {
// Shared configuration (same across all networks)
metadata: {
name: 'Private Strategy Vault',
symbol: 'pSV',
decimals: 9,
uri: 'https://raw.githubusercontent.com/midas-apps/midas-assets/refs/heads/main/solana/psv-metadata',
},
tokenAuthority: {
seed: 'psv-token-authority',
},
// Network-specific configurations
networks: {
mainnet: {
dataFeed: {
// Oracle tolerance: 1.04%
mode: 'manual',
minPrice: '0.99',
maxPrice: '1.03',
initialPrice: '1',
maxStaleness: 2592000,
},
minter: {
instantFee: '0',
instantDailyLimit: UNLIMITED,
variationTolerance: '0.2', // 20 bps
minAmount: '0',
firstMintMinMTokens: '0',
greenListEnforced: true,
feeReceiver: 'FCp91ChRdqcwZBp6N5R3ZrxbCMi7mQvvLntFZ27sBdkW',
tokensReceiver: 'HPdZXFUCcAmbnRh7sYHGWZFvhVhr5eC68vKuNcpo7So7',
paymentTokens: [
{
symbol: PaymentToken.wSOL,
fee: '0',
allowance: '10000000',
stable: true,
isFiat: false,
},
],
},
redeemer: {
instantFee: '0', // 100 bps (1%)
instantDailyLimit: UNLIMITED,
variationTolerance: '0.2', // 20 bps
minAmount: '0',
minFiatRedeemAmount: '10',
fiatFlatFee: '30',
greenListEnforced: true,
feeReceiver: '2rpnUduUCHRB9gCr3zmDQFgr5KSReLiN56QjNJ82D7vv',
tokensReceiver: 'HPdZXFUCcAmbnRh7sYHGWZFvhVhr5eC68vKuNcpo7So7',
requestRedeemer: '3DAz5Sdwaofm2FtM3bFSGdwihgmmwn4YhPNWLcXQ5BRc',
paymentTokens: [
{
symbol: PaymentToken.wSOL,
fee: '0',
allowance: '10000000',
stable: true,
isFiat: false,
},
],
},
grantRoles: {
tokenManagerAddress: '4qeuVmTwFRo1ZF7eVQXZNDNVFT33eav6KNP9xjSKQmZB',
vaultsManagerAddress: 'QRLkMrM5jfEmS6kmBBEgfDo97VariSWiuoCn1WkmBpj',
oracleManagerAddress: '28pe6ahpsFAo5DConqyw46Z1qAKtMNcqSKQ6iwwBYBcD',
metadataAuthority: '77F5WP7E9PE3cRbUXGZ8W8S2zvSGvb2WS7QuVGYpavug',
},
pauseFunctions: {
redeemer: [VaultActionIds.REDEEM_REQUEST_FIAT],
minter: [VaultActionIds.MINT_REQUEST],
},
},
},
};
19 changes: 19 additions & 0 deletions scripts/configs/tokens/payment-tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,23 @@ export const paymentTokenConfigs: Partial<Record<PaymentToken, PaymentTokenConfi
},
},
},
[PaymentToken.wSOL]: {
metadata: {
name: 'Wrapped SOL',
symbol: 'wSOL',
decimals: 9,
},
networks: {
mainnet: {
tokenAddress: 'So11111111111111111111111111111111111111112',
dataFeed: {
mode: 'manual',
maxPrice: '1.00',
minPrice: '0.9999',
initialPrice: '1',
maxStaleness: 365 * 86400,
},
},
},
},
};
8 changes: 8 additions & 0 deletions scripts/configs/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { z } from 'zod';

import { PaymentToken } from '@/common/tokenTypes';
import { VaultActionIds } from '@/test/constants/vaults.constants';

import { publicKeySchema } from './common-schemas';
import { grantRolesConfigSchema } from './roles-types';
Expand Down Expand Up @@ -169,20 +170,27 @@ export const redeemerVaultConfigSchema = z.object({
paymentTokens: z.array(paymentTokenConfigSchema),
});

export const pauseFunctionsConfigSchema = z.object({
minter: z.array(z.enum(VaultActionIds)).optional(),
redeemer: z.array(z.enum(VaultActionIds)).optional(),
});

export const tokenConfigSchema = z.object({
metadata: tokenMetadataSchema,
tokenAuthority: tokenAuthorityConfigSchema,
dataFeed: dataFeedConfigSchema,
minter: minterVaultConfigSchema,
redeemer: redeemerVaultConfigSchema,
grantRoles: grantRolesConfigSchema.optional(),
pauseFunctions: pauseFunctionsConfigSchema.optional(),
});

export const networkSpecificConfigSchema = z.object({
dataFeed: dataFeedConfigSchema,
minter: minterVaultConfigSchema,
redeemer: redeemerVaultConfigSchema,
grantRoles: grantRolesConfigSchema.optional(),
pauseFunctions: pauseFunctionsConfigSchema.optional(),
});

export const tokenConfigWithNetworksSchema = z.object({
Expand Down
9 changes: 1 addition & 8 deletions scripts/constants/pricing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,11 @@ import { MAX_U128 } from '@/test/constants/common.constants';
* All prices are stored and processed in base-9 format (9 decimal places)
*/

/**
* Number of decimal places used for price representation
* Prices are stored as integers with 9 decimal places
* Example: $1.00 is stored as 1_000_000_000
*/
export const PRICE_DECIMALS = 9;

/**
* Multiplier for converting decimal price strings to base-9 integers
* Used when converting config prices (e.g., "1.5") to on-chain format
*/
export const PRICE_MULTIPLIER = 10 ** PRICE_DECIMALS;
export const PRICE_MULTIPLIER = 10 ** 9;
Comment on lines 8 to +12
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The removal of PRICE_DECIMALS and hardcoding 10 ** 9 for PRICE_MULTIPLIER reduces maintainability. It's better to keep PRICE_DECIMALS as a constant. Additionally, given that manual feeds now operate with 8 decimals (as seen in scripts/deploy/feeds/manual.ts and scripts/utils/feedDeployment.ts), a new constant, such as MANUAL_FEED_DECIMALS = 8, should be introduced for clarity and consistency.

Suggested change
/**
* Multiplier for converting decimal price strings to base-9 integers
* Used when converting config prices (e.g., "1.5") to on-chain format
*/
export const PRICE_MULTIPLIER = 10 ** PRICE_DECIMALS;
export const PRICE_MULTIPLIER = 10 ** 9;
/**
* Number of decimal places used for price representation
* Prices are stored as integers with 9 decimal places
* Example: $1.00 is stored as 1_000_000_000
*/
export const PRICE_DECIMALS = 9;
/**
* Number of decimal places used for manual feed price representation.
*/
export const MANUAL_FEED_DECIMALS = 8;
/**
* Multiplier for converting decimal price strings to base-9 integers
* Used when converting config prices (e.g., "1.5") to on-chain format
*/
export const PRICE_MULTIPLIER = 10 ** PRICE_DECIMALS;


/**
* Value representing unlimited/no limit for daily limits and allowances.
Expand Down
Loading
Loading