Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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: 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
1 change: 1 addition & 0 deletions scripts/deploy/dataFeed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface CommonParams {
provider: AnchorProvider;
payer: Wallet;
network: string;
action?: string;
}

export const getDataFeedProgram = (provider: AnchorProvider) => {
Expand Down
Loading
Loading