From 54635cc283e7cc98c59bc5c5dc37a92a55ba2082 Mon Sep 17 00:00:00 2001 From: Bhargava Sai Macha Date: Tue, 27 Aug 2024 20:49:11 +0530 Subject: [PATCH] freeze, thaw and revoke ixs + execute in policy engine (#85) * add freeze, thaw and revoke * clippy * update testts * fix tests * fix tests * execute in policy engine (#86) * move execute to policy engine * fix tests * fix tests * fix ts build * fix lint * fix tests --- clients/rwa-token-sdk/package.json | 2 +- .../src/asset-controller/data.ts | 114 +-- .../src/asset-controller/instructions.ts | 300 +++++-- .../src/asset-controller/types.ts | 3 - .../src/asset-controller/utils.ts | 24 - .../src/classes/AssetController.ts | 20 +- .../src/identity-registry/types.ts | 2 + .../rwa-token-sdk/src/policy-engine/data.ts | 22 +- .../src/policy-engine/instructions.ts | 29 + .../rwa-token-sdk/src/policy-engine/types.ts | 3 + .../rwa-token-sdk/src/policy-engine/utils.ts | 39 +- .../src/programs/idls/AssetController.json | 804 ++++++++---------- .../src/programs/idls/PolicyEngine.json | 378 +++++++- .../programs/types/AssetControllerTypes.ts | 804 ++++++++---------- .../src/programs/types/PolicyEngineTypes.ts | 378 +++++++- clients/rwa-token-sdk/tests/e2e.test.ts | 106 ++- .../rwa-token-sdk/tests/extensions.test.ts | 24 +- clients/rwa-token-sdk/tests/policies.test.ts | 70 +- clients/rwa-token-sdk/tests/setup.ts | 4 +- clients/rwa-token-sdk/tests/tracker.test.ts | 30 +- programs/Cargo.lock | 33 +- programs/asset_controller/Cargo.toml | 8 +- programs/asset_controller/src/error.rs | 6 - .../src/instructions/account/freeze.rs | 52 ++ .../src/instructions/account/mod.rs | 6 +- .../src/instructions/account/thaw.rs | 52 ++ .../src/instructions/create.rs | 171 +++- .../src/instructions/extensions/close_mint.rs | 3 +- .../extensions/{memo.rs => disable_memo.rs} | 0 .../create.rs => extensions/enable_memo.rs} | 58 +- .../extensions/interest_bearing.rs | 7 +- .../src/instructions/extensions/mod.rs | 6 +- .../asset_controller/src/instructions/mod.rs | 2 - .../instructions/token/{void.rs => burn.rs} | 0 .../src/instructions/token/issue.rs | 68 +- .../src/instructions/token/mod.rs | 6 +- .../src/instructions/token/revoke.rs | 110 +++ .../src/instructions/update.rs | 10 +- programs/asset_controller/src/lib.rs | 39 +- programs/asset_controller/src/state/mod.rs | 10 - .../asset_controller/src/state/registry.rs | 3 + programs/asset_controller/src/utils.rs | 115 +-- .../src/instructions/account/delete.rs | 1 + .../src/instructions/registry/create.rs | 5 +- .../src/instructions/account/add.rs | 1 + .../src/instructions/account/create.rs | 3 +- .../identity_registry/src/state/account.rs | 2 +- programs/policy_engine/Cargo.toml | 8 +- programs/policy_engine/src/error.rs | 10 + .../src/instructions/engine/create.rs | 29 +- .../src/instructions/execute.rs | 87 +- .../policy_engine/src/instructions/mod.rs | 4 + .../policy_engine/src/instructions/tracker.rs | 39 + programs/policy_engine/src/lib.rs | 13 + programs/policy_engine/src/state/engine.rs | 9 + programs/policy_engine/src/state/mod.rs | 8 + .../src/state/track.rs | 5 +- programs/policy_engine/src/utils.rs | 96 ++- programs/rwa_utils/src/constants.rs | 1 + 59 files changed, 2650 insertions(+), 1592 deletions(-) create mode 100644 programs/asset_controller/src/instructions/account/freeze.rs create mode 100644 programs/asset_controller/src/instructions/account/thaw.rs rename programs/asset_controller/src/instructions/extensions/{memo.rs => disable_memo.rs} (100%) rename programs/asset_controller/src/instructions/{account/create.rs => extensions/enable_memo.rs} (57%) rename programs/asset_controller/src/instructions/token/{void.rs => burn.rs} (100%) create mode 100644 programs/asset_controller/src/instructions/token/revoke.rs rename programs/{asset_controller => policy_engine}/src/instructions/execute.rs (68%) create mode 100644 programs/policy_engine/src/instructions/tracker.rs rename programs/{asset_controller => policy_engine}/src/state/track.rs (91%) diff --git a/clients/rwa-token-sdk/package.json b/clients/rwa-token-sdk/package.json index 1ce104b..70a1b5d 100644 --- a/clients/rwa-token-sdk/package.json +++ b/clients/rwa-token-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@bridgesplit/rwa-token-sdk", - "version": "0.0.20", + "version": "0.0.27", "description": "RWA Token SDK for the development of permissioned tokens on SVM blockchains.", "homepage": "https://github.com/bridgesplit/rwa-token#readme", "main": "dist/index", diff --git a/clients/rwa-token-sdk/src/asset-controller/data.ts b/clients/rwa-token-sdk/src/asset-controller/data.ts index 30fe6e6..bada913 100644 --- a/clients/rwa-token-sdk/src/asset-controller/data.ts +++ b/clients/rwa-token-sdk/src/asset-controller/data.ts @@ -1,14 +1,10 @@ import { type AnchorProvider } from "@coral-xyz/anchor"; -import { type AssetControllerAccount, type TrackerAccount } from "./types"; +import { type AssetControllerAccount } from "./types"; import { getAssetControllerPda, getAssetControllerProgram, - getTrackerAccountPda, } from "./utils"; import { GetProgramAccountsFilter, PublicKey } from "@solana/web3.js"; -import { getPolicyAccount, getPolicyEngineAccount, PolicyAccount, PolicyEngineAccount } from "../policy-engine"; -import { DataAccount, DataRegistryAccount, getDataAccountsWithFilter, getDataRegistryAccount } from "../data-registry"; -import { getIdentityAccount, getIdentityRegistryAccount, IdentityAccount, IdentityRegistryAccount } from "../identity-registry"; /** * Retrieves a asset controller account associated with a specific asset mint. @@ -65,112 +61,4 @@ export async function getAssetControllerAccountsWithFilter( return assetAccounts.map((account) => assetProgram.coder.accounts.decode("AssetControllerAccount", account.account.data) ); -} - -/** - * Retrieves a tracker account pda associated with a specific asset mint and owner. - * @param assetMint - The string representation of the asset mint. - * @param owner - The string representation of the owner's public key. - * @returns A promise resolving to the fetched tracker account, or `undefined` if it doesn't exist. - */ -export async function getTrackerAccount( - assetMint: string, - owner: string, - provider: AnchorProvider -): Promise { - const assetProgram = getAssetControllerProgram(provider); - const trackerPda = getTrackerAccountPda(assetMint, owner); - return assetProgram.account.trackerAccount - .fetch(trackerPda) - .then((account) => account) - .catch(() => undefined); -} - -export const TRACKER_ACCOUNT_ASSET_MINT_OFFSET = 9; -export const TRACKER_ACCOUNT_OWNER_OFFSET = 41; - -/** - * Retrieves all tracker accounts associated with a specific asset mint. - * @param assetMint - The string representation of the asset mint. - * @returns A promise resolving to the fetched tracker accounts, or `undefined` if it doesn't exist. - */ -export async function getTrackerAccountsWithFilter( - filter: Omit, - provider: AnchorProvider -): Promise { - const { assetMint, owner } = filter; - const assetProgram = getAssetControllerProgram(provider); - const filters: GetProgramAccountsFilter[] = []; - if (assetMint) { - filters.push({ memcmp: { offset: TRACKER_ACCOUNT_ASSET_MINT_OFFSET, bytes: new PublicKey(assetMint).toBase58() } }); - } - if (owner) { - filters.push({ memcmp: { offset: TRACKER_ACCOUNT_OWNER_OFFSET, bytes: new PublicKey(owner).toBase58() } }); - } - const trackerAccounts = await provider.connection.getProgramAccounts(assetProgram.programId, { - filters, - }); - return trackerAccounts.map((account) => - assetProgram.coder.accounts.decode("TrackerAccount", account.account.data) - ); -} - -export interface RwaAccounts { - assetMint: string; - assetController?: AssetControllerAccount; - tracker?: TrackerAccount; - policyEngine?: PolicyEngineAccount; - policyAccount?: PolicyAccount; - dataRegistry?: DataRegistryAccount; - dataAccounts?: DataAccount[]; - identityRegistry?: IdentityRegistryAccount; - identity?: IdentityAccount; -} - - -/** - * Retrieves all RWA accounts associated with a specific asset mint. - * @param assetMints - The string representation of the asset mint. - * @returns A promise resolving to the fetched RWA accounts, or `undefined` if it doesn't exist. - */ -export async function getRwaAccountsWithMints( - assetMints: string[], - provider: AnchorProvider, - owner?: string, -): Promise { - const accounts: RwaAccounts[] = []; - for (const assetMint of assetMints) { - const assetController = getAssetControllerAccount(assetMint, provider); - const tracker = owner ? getTrackerAccount(assetMint, owner, provider) : undefined; - const policyEngine = getPolicyEngineAccount(assetMint, provider); - const policyAccount = getPolicyAccount(assetMint, provider); - const dataRegistry = getDataRegistryAccount(assetMint, provider); - const dataAccounts = getDataAccountsWithFilter({ assetMint }, provider); - const identityRegistry = getIdentityRegistryAccount(assetMint, provider); - const identity = owner ? getIdentityAccount(assetMint, owner, provider) : undefined; - - const [resolvedAssetController, resolvedTracker, resolvedPolicyEngine, resolvedPolicyAccount, resolvedDataRegistry, resolvedDataAccounts, resolvedIdentityRegistry, resolvedIdentity] = await Promise.all([ - assetController, - tracker, - policyEngine, - policyAccount, - dataRegistry, - dataAccounts, - identityRegistry, - identity - ]); - - accounts.push({ - assetMint, - assetController: resolvedAssetController, - tracker: resolvedTracker, - policyEngine: resolvedPolicyEngine, - policyAccount: resolvedPolicyAccount, - dataRegistry: resolvedDataRegistry, - dataAccounts: resolvedDataAccounts, - identityRegistry: resolvedIdentityRegistry, - identity: resolvedIdentity, - }); - } - return accounts; } \ No newline at end of file diff --git a/clients/rwa-token-sdk/src/asset-controller/instructions.ts b/clients/rwa-token-sdk/src/asset-controller/instructions.ts index 1859e22..c6b9fc0 100644 --- a/clients/rwa-token-sdk/src/asset-controller/instructions.ts +++ b/clients/rwa-token-sdk/src/asset-controller/instructions.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-call */ import { + ComputeBudgetProgram, Keypair, PublicKey, SystemProgram, @@ -8,16 +9,17 @@ import { TransactionInstruction, } from "@solana/web3.js"; import { - policyRegistryProgramId, - getCreatePolicyEngineIx, getPolicyEnginePda, getPolicyAccountPda, + getTrackerAccountPda, + policyEngineProgramId, + getCreateTrackerAccountIx, + getExtraMetasListPda, } from "../policy-engine"; -import { getCreateDataRegistryIx } from "../data-registry"; +import { dataRegistryProgramId, getDataRegistryPda } from "../data-registry"; import { identityRegistryProgramId, getCreateIdentityAccountIx, - getCreateIdentityRegistryIx, getIdentityAccountPda, getIdentityRegistryPda, } from "../identity-registry"; @@ -37,8 +39,6 @@ import { import { getAssetControllerProgram, getAssetControllerPda, - getExtraMetasListPda, - getTrackerAccountPda, assetControllerProgramId, getAssetControllerEventAuthority, } from "./utils"; @@ -81,6 +81,12 @@ export async function getCreateAssetControllerIx( systemProgram: SystemProgram.programId, tokenProgram: TOKEN_2022_PROGRAM_ID, authority: args.authority, + policyEngineAccount: getPolicyEnginePda(args.assetMint), + dataRegistryAccount: getDataRegistryPda(args.assetMint), + identityRegistryAccount: getIdentityRegistryPda(args.assetMint), + policyEngine: policyEngineProgramId, + identityRegistry: identityRegistryProgramId, + dataRegistry: dataRegistryProgramId, eventAuthority: getAssetControllerEventAuthority(), program: assetControllerProgramId, }) @@ -142,16 +148,14 @@ export type IssueTokenArgs = { export async function getIssueTokensIx( args: IssueTokenArgs, provider: AnchorProvider -): Promise { +): Promise { const assetProgram = getAssetControllerProgram(provider); const ix = await assetProgram.methods - .issueTokens({ - amount: new BN(args.amount), - to: new PublicKey(args.owner), - }) + .issueTokens(new BN(args.amount)) .accountsStrict({ authority: new PublicKey(args.authority), assetMint: new PublicKey(args.assetMint), + assetController: getAssetControllerPda(args.assetMint), tokenProgram: TOKEN_2022_PROGRAM_ID, tokenAccount: getAssociatedTokenAddressSync( new PublicKey(args.assetMint), @@ -159,9 +163,12 @@ export async function getIssueTokensIx( false, TOKEN_2022_PROGRAM_ID ), + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + systemProgram: SystemProgram.programId, + to: new PublicKey(args.owner), }) .instruction(); - return ix; + return [ix]; } export type VoidTokensArgs = { @@ -176,7 +183,7 @@ export async function getVoidTokensIx( const assetProgram = getAssetControllerProgram(provider); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument const ix = await assetProgram.methods - .voidTokens(new BN(args.amount)) + .burnTokens(new BN(args.amount)) .accountsStrict({ assetMint: new PublicKey(args.assetMint), tokenProgram: TOKEN_2022_PROGRAM_ID, @@ -196,10 +203,11 @@ export type TransferTokensArgs = { from: string; to: string; amount: number; + assetMint: string; decimals: number; message?: string; createTa?: boolean; -} & CommonArgs; +} /** * Creates a transaction instruction to transfer a token between addresses with transfer controls. @@ -211,11 +219,6 @@ export async function getTransferTokensIxs( provider: AnchorProvider ): Promise { const remainingAccounts = [ - { - pubkey: policyRegistryProgramId, - isWritable: false, - isSigner: false, - }, { pubkey: getPolicyEnginePda(args.assetMint), isWritable: false, @@ -233,7 +236,7 @@ export async function getTransferTokensIxs( }, { pubkey: getIdentityAccountPda(args.assetMint, args.to), - isWritable: true, + isWritable: false, isSigner: false, }, { @@ -243,7 +246,7 @@ export async function getTransferTokensIxs( }, { pubkey: getPolicyAccountPda(args.assetMint), - isWritable: false, + isWritable: true, isSigner: false, }, { @@ -257,11 +260,12 @@ export async function getTransferTokensIxs( isSigner: false, }, { - pubkey: assetControllerProgramId, + pubkey: policyEngineProgramId, isWritable: false, isSigner: false, }, ]; + const ixs: TransactionInstruction[] = []; try { const ta = await getAccount(provider.connection, getAssociatedTokenAddressSync( @@ -283,7 +287,7 @@ export async function getTransferTokensIxs( } } catch (error) { if (args.createTa) { - ixs.push(createAssociatedTokenAccountInstruction(new PublicKey(args.payer), getAssociatedTokenAddressSync( + ixs.push(createAssociatedTokenAccountInstruction(new PublicKey(args.from), getAssociatedTokenAddressSync( new PublicKey(args.assetMint), new PublicKey(args.to), true, @@ -291,6 +295,7 @@ export async function getTransferTokensIxs( ), new PublicKey(args.to), new PublicKey(args.assetMint), TOKEN_2022_PROGRAM_ID)); } } + const ix = createTransferCheckedInstruction( getAssociatedTokenAddressSync( new PublicKey(args.assetMint), @@ -317,42 +322,6 @@ export async function getTransferTokensIxs( return ixs; } -export type CreateTokenAccountArgs = { - owner: string; - memoTransfer?: boolean; -} & CommonArgs; - -export async function getCreateTokenAccountIx( - args: CreateTokenAccountArgs, - provider: AnchorProvider -): Promise { - const assetProgram = getAssetControllerProgram(provider); - const ix = await assetProgram.methods - .createTokenAccount({ - memoTransfer: args.memoTransfer || false, - }) - .accountsStrict({ - payer: args.payer, - assetMint: args.assetMint, - owner: args.owner, - tokenProgram: TOKEN_2022_PROGRAM_ID, - assetController: getAssetControllerPda(args.assetMint), - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - systemProgram: SystemProgram.programId, - tokenAccount: getAssociatedTokenAddressSync( - new PublicKey(args.assetMint), - new PublicKey(args.owner), - false, - TOKEN_2022_PROGRAM_ID - ), - trackerAccount: getTrackerAccountPda(args.assetMint, args.owner), - program: assetControllerProgramId, - eventAuthority: getAssetControllerEventAuthority(), - }) - .instruction(); - return ix; -} - /** Args used to generate new asset controller */ export type SetupAssetControllerArgs = { authority: string; @@ -383,28 +352,11 @@ export async function getSetupAssetControllerIxs( updatedArgs, provider ); - // Get policy registry create ix - const policyEngineCreateIx = await getCreatePolicyEngineIx( - updatedArgs, - provider - ); - // Get data registry create ix - const dataRegistryCreateIx = await getCreateDataRegistryIx( - updatedArgs, - provider - ); - // Get identity registry create ix - const identityRegistryCreateIx = await getCreateIdentityRegistryIx( - updatedArgs, - provider - ); return { ixs: [ + ComputeBudgetProgram.setComputeUnitLimit({units: 450_000}), assetControllerCreateIx, - policyEngineCreateIx, - dataRegistryCreateIx, - identityRegistryCreateIx, ], signers: [mintKp], }; @@ -417,7 +369,6 @@ export type SetupUserArgs = { signer: string; assetMint: string; level: number; - memoTransfer?: boolean; }; /** @@ -441,9 +392,16 @@ export async function getSetupUserIxs( }, provider ); - const createTaIx = await getCreateTokenAccountIx(args, provider); + const trackerAccountIx = await getCreateTrackerAccountIx( + { + payer: args.payer, + owner: args.owner, + assetMint: args.assetMint, + }, + provider + ); return { - ixs: [identityAccountIx, createTaIx], + ixs: [identityAccountIx, trackerAccountIx], signers: [], }; } @@ -480,8 +438,36 @@ export async function getUpdateInterestBearingMintRateIx( export type MemoTranferArgs = { owner: string; tokenAccount: string; + assetMint: string; }; +/** + * Generate Instructions to disable memo transfer + * @param args - {@link MemoTranferArgs} + * @returns - {@link TransactionInstruction} + * */ +export async function getEnableMemoTransferIx( + args: MemoTranferArgs, + provider: AnchorProvider +): Promise { + const assetProgram = getAssetControllerProgram(provider); + const ix = await assetProgram.methods + .enableMemoTransfer() + .accountsStrict({ + owner: new PublicKey(args.owner), + tokenAccount: new PublicKey(args.tokenAccount), + tokenProgram: TOKEN_2022_PROGRAM_ID, + assetMint: new PublicKey(args.assetMint), + program: assetControllerProgramId, + eventAuthority: getAssetControllerEventAuthority(), + payer: new PublicKey(args.owner), + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + systemProgram: SystemProgram.programId, + }) + .instruction(); + return ix; +} + /** * Generate Instructions to disable memo transfer * @param args - {@link MemoTranferArgs} @@ -529,4 +515,158 @@ export async function getCloseMintIx( }) .instruction(); return ix; +} + +export type FreezeTokenArgs = { + authority: string; + owner: string; +} & CommonArgs; + +/** + * Generate Instructions to freeze token account + * @param args - {@link FreezeTokenArgs} + * @returns - {@link TransactionInstruction} + */ +export async function getFreezeTokenIx( + args: FreezeTokenArgs, + provider: AnchorProvider +): Promise { + const assetProgram = getAssetControllerProgram(provider); + const ix = await assetProgram.methods + .freezeTokenAccount() + .accountsStrict({ + authority: new PublicKey(args.authority), + assetMint: new PublicKey(args.assetMint), + tokenProgram: TOKEN_2022_PROGRAM_ID, + assetController: getAssetControllerPda(args.assetMint), + tokenAccount: getAssociatedTokenAddressSync( + new PublicKey(args.assetMint), + new PublicKey(args.owner), + false, + TOKEN_2022_PROGRAM_ID + ), + }) + .instruction(); + return ix; +} + +/** + * Generate Instructions to thaw token account + * @param args - {@link FreezeTokenArgs} + * @returns - {@link TransactionInstruction} + * */ +export async function getThawTokenIx( + args: FreezeTokenArgs, + provider: AnchorProvider +): Promise { + const assetProgram = getAssetControllerProgram(provider); + const ix = await assetProgram.methods + .thawTokenAccount() + .accountsStrict({ + authority: new PublicKey(args.authority), + assetMint: new PublicKey(args.assetMint), + tokenProgram: TOKEN_2022_PROGRAM_ID, + assetController: getAssetControllerPda(args.assetMint), + tokenAccount: getAssociatedTokenAddressSync( + new PublicKey(args.assetMint), + new PublicKey(args.owner), + false, + TOKEN_2022_PROGRAM_ID + ), + }) + .instruction(); + return ix; +} + +export type RevokeTokensArgs = { + amount: number; + owner: string; + authority: string; + assetMint: string; +}; + +/** + * Revoke tokens from a user + * @param args - {@link RevokeTokensArgs} + * @returns - {@link TransactionInstruction} + * */ +export async function getRevokeTokensIx( + args: RevokeTokensArgs, + provider: AnchorProvider +): Promise { + const assetProgram = getAssetControllerProgram(provider); + const remainingAccounts = [ + { + pubkey: getPolicyEnginePda(args.assetMint), + isWritable: false, + isSigner: false, + }, + { + pubkey: identityRegistryProgramId, + isWritable: false, + isSigner: false, + }, + { + pubkey: getIdentityRegistryPda(args.assetMint), + isWritable: false, + isSigner: false, + }, + { + pubkey: getIdentityAccountPda(args.assetMint, getAssetControllerPda(args.assetMint).toString()), + isWritable: false, + isSigner: false, + }, + { + pubkey: getTrackerAccountPda(args.assetMint, getAssetControllerPda(args.assetMint).toString()), + isWritable: true, + isSigner: false, + }, + { + pubkey: getPolicyAccountPda(args.assetMint), + isWritable: true, + isSigner: false, + }, + { + pubkey: SYSVAR_INSTRUCTIONS_PUBKEY, + isWritable: false, + isSigner: false, + }, + { + pubkey: policyEngineProgramId, + isWritable: false, + isSigner: false, + }, + { + pubkey: getExtraMetasListPda(args.assetMint), + isWritable: false, + isSigner: false, + }, + ]; + const ixs: TransactionInstruction[] = []; + const ix = await assetProgram.methods + .revokeTokens(new BN(args.amount)) + .accountsStrict({ + authority: new PublicKey(args.authority), + assetMint: new PublicKey(args.assetMint), + tokenProgram: TOKEN_2022_PROGRAM_ID, + assetController: getAssetControllerPda(args.assetMint), + revokeTokenAccount: getAssociatedTokenAddressSync( + new PublicKey(args.assetMint), + new PublicKey(args.owner), + false, + TOKEN_2022_PROGRAM_ID + ), + authorityTokenAccount: getAssociatedTokenAddressSync( + new PublicKey(args.assetMint), + getAssetControllerPda(args.assetMint), + true, + TOKEN_2022_PROGRAM_ID + ), + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + systemProgram: SystemProgram.programId, + }) + .remainingAccounts(remainingAccounts) + .instruction(); + ixs.push(ix); + return ixs; } \ No newline at end of file diff --git a/clients/rwa-token-sdk/src/asset-controller/types.ts b/clients/rwa-token-sdk/src/asset-controller/types.ts index 6554cb4..dc66579 100644 --- a/clients/rwa-token-sdk/src/asset-controller/types.ts +++ b/clients/rwa-token-sdk/src/asset-controller/types.ts @@ -4,6 +4,3 @@ import { AssetControllerIdlTypes } from "../programs"; /** Represents on chain asset controller account. */ export type AssetControllerAccount = IdlAccounts["assetControllerAccount"]; - -/** Represents on chain tracker account pda. */ -export type TrackerAccount = IdlAccounts["trackerAccount"]; diff --git a/clients/rwa-token-sdk/src/asset-controller/utils.ts b/clients/rwa-token-sdk/src/asset-controller/utils.ts index 1959a9a..f8bd220 100644 --- a/clients/rwa-token-sdk/src/asset-controller/utils.ts +++ b/clients/rwa-token-sdk/src/asset-controller/utils.ts @@ -1,7 +1,6 @@ import { PublicKey } from "@solana/web3.js"; import { type Idl, Program, type Provider, utils } from "@coral-xyz/anchor"; import { AssetControllerIdl, AssetControllerIdlTypes } from "../programs"; -import { utf8 } from "@coral-xyz/anchor/dist/cjs/utils/bytes"; export const assetControllerProgramId = new PublicKey( "acpcFrzEYKjVLvZGWueTV8vyDjhu3oKC7sN38QELLan" @@ -26,29 +25,6 @@ export const getAssetControllerPda = (assetMint: string) => assetControllerProgramId )[0]; -/** - * Retrieves the asset controller's metadata pda account for a specific asset mint. - * @param assetMint - The string representation of the asset's mint address. - * @returns The asset controller's extra metadata pda. - */ -export const getExtraMetasListPda = (assetMint: string) => - PublicKey.findProgramAddressSync( - [utf8.encode("extra-account-metas"), new PublicKey(assetMint).toBuffer()], - assetControllerProgramId - )[0]; - -/** - * Retrieves the tracker pda for a specific asset controller mint and owner. - * @param assetMint - The string representation of the asset's mint address. - * @param owner - The string representation of asset's owner. - * @returns The asset controller's tracker pda. - */ -export const getTrackerAccountPda = (assetMint: string, owner: string) => - PublicKey.findProgramAddressSync( - [new PublicKey(assetMint).toBuffer(), new PublicKey(owner).toBuffer()], - assetControllerProgramId - )[0]; - export const getAssetControllerEventAuthority = () => PublicKey.findProgramAddressSync( [utils.bytes.utf8.encode("__event_authority")], assetControllerProgramId diff --git a/clients/rwa-token-sdk/src/classes/AssetController.ts b/clients/rwa-token-sdk/src/classes/AssetController.ts index ec11b34..2a4aa70 100644 --- a/clients/rwa-token-sdk/src/classes/AssetController.ts +++ b/clients/rwa-token-sdk/src/classes/AssetController.ts @@ -8,14 +8,13 @@ import { getSetupAssetControllerIxs, getVoidTokensIx, getAssetControllerPda, - getTrackerAccountPda, - getExtraMetasListPda, getUpdateAssetMetadataIx, UpdateAssetMetadataArgs, getTransferTokensIxs, } from "../asset-controller"; import { type IxReturn } from "../utils"; import { type RwaClient } from "./Client"; +import { getExtraMetasListPda } from "../policy-engine"; /** * Represents the client for Asset Controller for an RWA. @@ -65,12 +64,12 @@ export class AssetController { */ async issueTokenIxns( IssueArgs: IssueTokenArgs - ): Promise { - const issueTokensIx = await getIssueTokensIx( + ): Promise { + const issueTokensIxs = await getIssueTokensIx( IssueArgs, this.rwaClient.provider ); - return issueTokensIx; + return issueTokensIxs; } /** @@ -116,15 +115,4 @@ export class AssetController { getExtraMetasListPda(assetMint: string): PublicKey { return getExtraMetasListPda(assetMint); } - - /** - * Retrieves the tracker pda for a specific asset controller mint and owner. - * Tracks the transfers happening from user account. Important for enforcing policies. - * @param assetMint - The string representation of the asset's mint address. - * @param owner - The string representation of asset's owner. - * @returns The asset controller's tracker pda. - */ - getTrackerAccountPda(assetMint: string, owner: string): PublicKey { - return getTrackerAccountPda(assetMint, owner); - } } diff --git a/clients/rwa-token-sdk/src/identity-registry/types.ts b/clients/rwa-token-sdk/src/identity-registry/types.ts index 011485b..c39c137 100644 --- a/clients/rwa-token-sdk/src/identity-registry/types.ts +++ b/clients/rwa-token-sdk/src/identity-registry/types.ts @@ -8,3 +8,5 @@ export type IdentityRegistryAccount = /** Represents on chain identity account. */ export type IdentityAccount = IdlAccounts["identityAccount"]; + + \ No newline at end of file diff --git a/clients/rwa-token-sdk/src/policy-engine/data.ts b/clients/rwa-token-sdk/src/policy-engine/data.ts index 8788a04..d2fe1fb 100644 --- a/clients/rwa-token-sdk/src/policy-engine/data.ts +++ b/clients/rwa-token-sdk/src/policy-engine/data.ts @@ -1,6 +1,6 @@ import { type AnchorProvider } from "@coral-xyz/anchor"; -import { type PolicyEngineAccount, type PolicyAccount } from "./types"; -import { getPolicyAccountPda, getPolicyEnginePda, getPolicyEngineProgram } from "./utils"; +import { type PolicyEngineAccount, type PolicyAccount, TrackerAccount } from "./types"; +import { getPolicyAccountPda, getPolicyEnginePda, getPolicyEngineProgram, getTrackerAccountPda } from "./utils"; import { GetProgramAccountsFilter, PublicKey } from "@solana/web3.js"; /** @@ -59,4 +59,22 @@ export async function getPolicyAccount(assetMint: string, provider: AnchorProvid const policyEngineProgram = getPolicyEngineProgram(provider); const policyAccountPda = getPolicyAccountPda(assetMint); return policyEngineProgram.account.policyAccount.fetch(policyAccountPda); +} + + + +/** + * Retrieves a tracker account pda associated with a specific asset mint and owner. + * @param assetMint - The string representation of the asset mint. + * @param owner - The string representation of the owner's public key. + * @returns A promise resolving to the fetched tracker account, or `undefined` if it doesn't exist. + */ +export async function getTrackerAccount( + assetMint: string, + owner: string, + provider: AnchorProvider +): Promise { + const policyEngineProgram = getPolicyEngineProgram(provider); + const trackerPda = getTrackerAccountPda(assetMint, owner); + return await policyEngineProgram.account.trackerAccount.fetch(trackerPda); } \ No newline at end of file diff --git a/clients/rwa-token-sdk/src/policy-engine/instructions.ts b/clients/rwa-token-sdk/src/policy-engine/instructions.ts index 5af7424..c0c9a9c 100644 --- a/clients/rwa-token-sdk/src/policy-engine/instructions.ts +++ b/clients/rwa-token-sdk/src/policy-engine/instructions.ts @@ -8,6 +8,8 @@ import { getPolicyAccountPda, getPolicyEnginePda, getPolicyEngineProgram, + getPolicyEnginerEventAuthority, + getTrackerAccountPda, } from "./utils"; import { type PolicyType, type IdentityFilter } from "./types"; import { type AnchorProvider } from "@coral-xyz/anchor"; @@ -149,3 +151,30 @@ export async function getCreatePolicyAccountIx( signers: [], }; } + +export interface CreateTrackerAccountArgs { + payer: string; + owner: string; + assetMint: string; +} + +export async function getCreateTrackerAccountIx( + args: CreateTrackerAccountArgs, + provider: AnchorProvider +): Promise { + const policyProgram = getPolicyEngineProgram(provider); + const trackerAccount = getTrackerAccountPda(args.assetMint, args.owner); + const ix = await policyProgram.methods + .createTrackerAccount() + .accountsStrict({ + payer: args.payer, + trackerAccount, + owner: new PublicKey(args.owner), + systemProgram: SystemProgram.programId, + program: policyProgram.programId, + assetMint: new PublicKey(args.assetMint), + eventAuthority: getPolicyEnginerEventAuthority(), + }) + .instruction(); + return ix; +} \ No newline at end of file diff --git a/clients/rwa-token-sdk/src/policy-engine/types.ts b/clients/rwa-token-sdk/src/policy-engine/types.ts index 4dfbcd1..d0340b8 100644 --- a/clients/rwa-token-sdk/src/policy-engine/types.ts +++ b/clients/rwa-token-sdk/src/policy-engine/types.ts @@ -17,3 +17,6 @@ export type IdentityFilterComparisonType = /** Represents on chain policy. */ export type PolicyType = IdlTypes["policyType"]; + +/** Represents on chain tracker account pda. */ +export type TrackerAccount = IdlAccounts["trackerAccount"]; diff --git a/clients/rwa-token-sdk/src/policy-engine/utils.ts b/clients/rwa-token-sdk/src/policy-engine/utils.ts index 4cdfecd..8c3653e 100644 --- a/clients/rwa-token-sdk/src/policy-engine/utils.ts +++ b/clients/rwa-token-sdk/src/policy-engine/utils.ts @@ -1,10 +1,11 @@ -import { type Idl, Program, type Provider } from "@coral-xyz/anchor"; +import { type Idl, Program, type Provider, utils } from "@coral-xyz/anchor"; import { PolicyEngineIdl } from "../programs/idls"; import { PublicKey } from "@solana/web3.js"; import { type PolicyEngineIdlTypes } from "../programs/types"; +import { utf8 } from "@coral-xyz/anchor/dist/cjs/utils/bytes"; /** Program address for the policy engine program. */ -export const policyRegistryProgramId = new PublicKey( +export const policyEngineProgramId = new PublicKey( "po1cPf1eyUJJPqULw4so3T4JU9pdFn83CDyuLEKFAau" ); @@ -28,7 +29,7 @@ export const getPolicyEngineProgram = (provider: Provider) => export const getPolicyEnginePda = (assetMint: string) => PublicKey.findProgramAddressSync( [new PublicKey(assetMint).toBuffer()], - policyRegistryProgramId + policyEngineProgramId )[0]; /** @@ -39,5 +40,35 @@ export const getPolicyEnginePda = (assetMint: string) => export const getPolicyAccountPda = (assetMint: string) => PublicKey.findProgramAddressSync( [getPolicyEnginePda(assetMint).toBuffer()], - policyRegistryProgramId + policyEngineProgramId )[0]; + +/** + * Retrieves the tracker pda for a specific asset controller mint and owner. + * @param assetMint - The string representation of the asset's mint address. + * @param owner - The string representation of asset's owner. + * @returns The asset controller's tracker pda. + */ +export const getTrackerAccountPda = (assetMint: string, owner: string) => + PublicKey.findProgramAddressSync( + [new PublicKey(assetMint).toBuffer(), new PublicKey(owner).toBuffer()], + policyEngineProgramId + )[0]; + +export const getPolicyEnginerEventAuthority = () => PublicKey.findProgramAddressSync( + [utils.bytes.utf8.encode("__event_authority")], + policyEngineProgramId +)[0]; + + +/** + * Retrieves the asset controller's metadata pda account for a specific asset mint. + * @param assetMint - The string representation of the asset's mint address. + * @returns The asset controller's extra metadata pda. + */ +export const getExtraMetasListPda = (assetMint: string) => + PublicKey.findProgramAddressSync( + [utf8.encode("extra-account-metas"), new PublicKey(assetMint).toBuffer()], + policyEngineProgramId + )[0]; + diff --git a/clients/rwa-token-sdk/src/programs/idls/AssetController.json b/clients/rwa-token-sdk/src/programs/idls/AssetController.json index 878e081..7bcbfaa 100644 --- a/clients/rwa-token-sdk/src/programs/idls/AssetController.json +++ b/clients/rwa-token-sdk/src/programs/idls/AssetController.json @@ -7,6 +7,99 @@ "description": "The Asset Controller Program (ACP) enables core asset management functionality for newly issued assets, including transfer controls and transaction privacy." }, "instructions": [ + { + "name": "burn_tokens", + "docs": [ + "burn shares of the rwa asset" + ], + "discriminator": [ + 76, + 15, + 51, + 254, + 229, + 215, + 121, + 66 + ], + "accounts": [ + { + "name": "owner", + "signer": true + }, + { + "name": "asset_mint", + "writable": true + }, + { + "name": "token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "owner" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "asset_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "token_program", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, { "name": "close_mint_account", "docs": [ @@ -202,11 +295,6 @@ { "name": "authority" }, - { - "name": "asset_mint", - "writable": true, - "signer": true - }, { "name": "asset_controller", "writable": true, @@ -220,44 +308,113 @@ } }, { - "name": "extra_metas_account", + "name": "asset_mint", "writable": true, + "signer": true + }, + { + "name": "extra_metas_account", + "writable": true + }, + { + "name": "policy_engine_account", + "writable": true + }, + { + "name": "identity_registry_account", + "writable": true + }, + { + "name": "data_registry_account", + "writable": true + }, + { + "name": "policy_engine", + "address": "po1cPf1eyUJJPqULw4so3T4JU9pdFn83CDyuLEKFAau" + }, + { + "name": "identity_registry", + "address": "idtynCMYbdisCTv4FrCWPSQboZb1uM4TV2cPi79yxQf" + }, + { + "name": "data_registry", + "address": "dataeP5X1e7XsWN1ovDSEDP5cqaEUnKBmHE5iZhXPVw" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "name": "event_authority", "pda": { "seeds": [ { "kind": "const", "value": [ + 95, + 95, + 101, + 118, 101, - 120, + 110, 116, - 114, - 97, - 45, + 95, 97, - 99, - 99, - 111, 117, - 110, 116, - 45, - 109, - 101, + 104, + 111, + 114, + 105, 116, - 97, - 115 + 121 ] - }, - { - "kind": "account", - "path": "asset_mint" } ] } }, { - "name": "system_program", - "address": "11111111111111111111111111111111" + "name": "program" + } + ], + "args": [ + { + "name": "args", + "type": { + "defined": { + "name": "CreateAssetControllerArgs" + } + } + } + ] + }, + { + "name": "disable_memo_transfer", + "docs": [ + "memo transfer disable" + ], + "discriminator": [ + 68, + 156, + 197, + 9, + 43, + 91, + 114, + 19 + ], + "accounts": [ + { + "name": "owner", + "signer": true + }, + { + "name": "token_account", + "writable": true }, { "name": "token_program", @@ -296,31 +453,22 @@ "name": "program" } ], - "args": [ - { - "name": "args", - "type": { - "defined": { - "name": "CreateAssetControllerArgs" - } - } - } - ] + "args": [] }, { - "name": "create_token_account", + "name": "enable_memo_transfer", "docs": [ - "create a token account" + "memo transfer enable" ], "discriminator": [ - 147, - 241, - 123, - 100, - 244, - 132, - 174, - 118 + 186, + 78, + 97, + 172, + 71, + 172, + 99, + 0 ], "accounts": [ { @@ -329,7 +477,8 @@ "signer": true }, { - "name": "owner" + "name": "owner", + "signer": true }, { "name": "asset_mint" @@ -391,37 +540,6 @@ } } }, - { - "name": "tracker_account", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "asset_mint" - }, - { - "kind": "account", - "path": "owner" - } - ] - } - }, - { - "name": "asset_controller", - "pda": { - "seeds": [ - { - "kind": "account", - "path": "asset_mint" - } - ] - } - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - }, { "name": "token_program", "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" @@ -430,6 +548,10 @@ "name": "associated_token_program", "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, { "name": "event_authority", "pda": { @@ -463,140 +585,105 @@ "name": "program" } ], - "args": [ - { - "name": "args", - "type": { - "defined": { - "name": "CreateTokenAccountArgs" - } - } - } - ] + "args": [] }, { - "name": "disable_memo_transfer", + "name": "freeze_token_account", "docs": [ - "memo transfer disable" + "freeze token account" ], "discriminator": [ - 68, - 156, - 197, - 9, - 43, - 91, - 114, - 19 + 138, + 168, + 178, + 109, + 205, + 224, + 209, + 93 ], "accounts": [ { - "name": "owner", + "name": "authority", "signer": true }, { - "name": "token_account", + "name": "asset_mint", "writable": true }, { - "name": "token_program", - "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" - }, - { - "name": "event_authority", + "name": "asset_controller", "pda": { "seeds": [ { - "kind": "const", - "value": [ - 95, - 95, - 101, - 118, - 101, - 110, - 116, - 95, - 97, - 117, - 116, - 104, - 111, - 114, - 105, - 116, - 121 - ] + "kind": "account", + "path": "asset_mint" } ] } }, { - "name": "program" + "name": "token_account", + "writable": true + }, + { + "name": "token_program", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" } ], "args": [] }, { - "name": "execute_transaction", + "name": "issue_tokens", "docs": [ - "execute transfer hook" + "issue shares of the rwa asset" ], "discriminator": [ - 105, - 37, - 101, - 197, - 75, - 251, - 102, - 26 + 40, + 207, + 145, + 106, + 249, + 54, + 23, + 179 ], "accounts": [ { - "name": "source_account", + "name": "authority", + "writable": true, + "signer": true + }, + { + "name": "asset_mint", + "writable": true + }, + { + "name": "asset_controller", + "pda": { + "seeds": [ + { + "kind": "account", + "path": "asset_mint" + } + ] + } + }, + { + "name": "to" + }, + { + "name": "token_account", + "writable": true, "pda": { "seeds": [ { "kind": "account", - "path": "owner_delegate" + "path": "to" }, { - "kind": "const", - "value": [ - 6, - 221, - 246, - 225, - 238, - 117, - 143, - 222, - 24, - 66, - 93, - 188, - 228, - 108, - 205, - 218, - 182, - 26, - 252, - 77, - 131, - 185, - 13, - 39, - 254, - 189, - 249, - 40, - 216, - 161, - 139, - 252 - ] + "kind": "account", + "path": "token_program" }, { "kind": "account", @@ -639,80 +726,20 @@ 248, 89 ] - } - } - }, - { - "name": "asset_mint" - }, - { - "name": "destination_account" - }, - { - "name": "owner_delegate" - }, - { - "name": "extra_metas_account", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 101, - 120, - 116, - 114, - 97, - 45, - 97, - 99, - 99, - 111, - 117, - 110, - 116, - 45, - 109, - 101, - 116, - 97, - 115 - ] - }, - { - "kind": "account", - "path": "asset_mint" - } - ] + } } }, { - "name": "policy_engine", - "address": "po1cPf1eyUJJPqULw4so3T4JU9pdFn83CDyuLEKFAau" - }, - { - "name": "policy_engine_account" - }, - { - "name": "identity_registry", - "address": "idtynCMYbdisCTv4FrCWPSQboZb1uM4TV2cPi79yxQf" - }, - { - "name": "identity_registry_account" - }, - { - "name": "identity_account", - "writable": true - }, - { - "name": "tracker_account", - "writable": true + "name": "token_program", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" }, { - "name": "policy_account" + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" }, { - "name": "instructions_program" + "name": "system_program", + "address": "11111111111111111111111111111111" } ], "args": [ @@ -723,23 +750,24 @@ ] }, { - "name": "issue_tokens", + "name": "revoke_tokens", "docs": [ - "issue shares of the rwa asset" + "revoke shares of the rwa asset" ], "discriminator": [ - 40, - 207, - 145, - 106, - 249, - 54, - 23, - 179 + 215, + 42, + 15, + 134, + 173, + 80, + 33, + 21 ], "accounts": [ { "name": "authority", + "writable": true, "signer": true }, { @@ -747,13 +775,24 @@ "writable": true }, { - "name": "token_account", + "name": "asset_controller", + "pda": { + "seeds": [ + { + "kind": "account", + "path": "asset_mint" + } + ] + } + }, + { + "name": "authority_token_account", "writable": true, "pda": { "seeds": [ { - "kind": "arg", - "path": "args.to" + "kind": "account", + "path": "asset_controller" }, { "kind": "account", @@ -803,22 +842,76 @@ } } }, + { + "name": "revoke_token_account", + "writable": true + }, { "name": "token_program", "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" } ], "args": [ { - "name": "args", - "type": { - "defined": { - "name": "IssueTokensArgs" - } - } + "name": "amount", + "type": "u64" } ] }, + { + "name": "thaw_token_account", + "docs": [ + "thaw token account" + ], + "discriminator": [ + 199, + 172, + 96, + 93, + 244, + 252, + 137, + 171 + ], + "accounts": [ + { + "name": "authority", + "signer": true + }, + { + "name": "asset_mint", + "writable": true + }, + { + "name": "asset_controller", + "pda": { + "seeds": [ + { + "kind": "account", + "path": "asset_mint" + } + ] + } + }, + { + "name": "token_account", + "writable": true + }, + { + "name": "token_program", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + } + ], + "args": [] + }, { "name": "update_interest_bearing_mint_rate", "docs": [ @@ -990,99 +1083,6 @@ } } ] - }, - { - "name": "void_tokens", - "docs": [ - "void shares of the rwa asset" - ], - "discriminator": [ - 101, - 147, - 63, - 157, - 106, - 103, - 119, - 74 - ], - "accounts": [ - { - "name": "owner", - "signer": true - }, - { - "name": "asset_mint", - "writable": true - }, - { - "name": "token_account", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "owner" - }, - { - "kind": "account", - "path": "token_program" - }, - { - "kind": "account", - "path": "asset_mint" - } - ], - "program": { - "kind": "const", - "value": [ - 140, - 151, - 37, - 143, - 78, - 36, - 137, - 241, - 187, - 61, - 16, - 41, - 20, - 142, - 13, - 131, - 11, - 90, - 19, - 153, - 218, - 255, - 16, - 132, - 4, - 142, - 123, - 216, - 219, - 233, - 248, - 89 - ] - } - } - }, - { - "name": "token_program", - "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" - } - ], - "args": [ - { - "name": "amount", - "type": "u64" - } - ] } ], "accounts": [ @@ -1098,19 +1098,6 @@ 52, 105 ] - }, - { - "name": "TrackerAccount", - "discriminator": [ - 83, - 95, - 166, - 148, - 57, - 30, - 90, - 210 - ] } ], "events": [ @@ -1186,21 +1173,6 @@ "code": 6008, "name": "InvalidPdaPassedIn", "msg": "Pda passed in for transfer is wrong" - }, - { - "code": 6009, - "name": "InvalidCpiTransferProgram", - "msg": "Invalid cpi program in transfer" - }, - { - "code": 6010, - "name": "InvalidCpiTransferAmount", - "msg": "Invalid cpi amount in transfer" - }, - { - "code": 6011, - "name": "InvalidCpiTransferMint", - "msg": "Invalid cpi mint in transfer" } ], "types": [ @@ -1311,18 +1283,6 @@ ] } }, - { - "name": "CreateTokenAccountArgs", - "type": { - "kind": "struct", - "fields": [ - { - "name": "memo_transfer", - "type": "bool" - } - ] - } - }, { "name": "ExtensionMetadataEvent", "type": { @@ -1343,68 +1303,6 @@ ] } }, - { - "name": "IssueTokensArgs", - "type": { - "kind": "struct", - "fields": [ - { - "name": "amount", - "type": "u64" - }, - { - "name": "to", - "type": "pubkey" - } - ] - } - }, - { - "name": "TrackerAccount", - "type": { - "kind": "struct", - "fields": [ - { - "name": "version", - "type": "u8" - }, - { - "name": "asset_mint", - "type": "pubkey" - }, - { - "name": "owner", - "type": "pubkey" - }, - { - "name": "transfers", - "type": { - "vec": { - "defined": { - "name": "Transfer" - } - } - } - } - ] - } - }, - { - "name": "Transfer", - "type": { - "kind": "struct", - "fields": [ - { - "name": "amount", - "type": "u64" - }, - { - "name": "timestamp", - "type": "i64" - } - ] - } - }, { "name": "UpdateAssetMetadataArgs", "type": { diff --git a/clients/rwa-token-sdk/src/programs/idls/PolicyEngine.json b/clients/rwa-token-sdk/src/programs/idls/PolicyEngine.json index 4c2100b..90c410d 100644 --- a/clients/rwa-token-sdk/src/programs/idls/PolicyEngine.json +++ b/clients/rwa-token-sdk/src/programs/idls/PolicyEngine.json @@ -196,6 +196,88 @@ } ] }, + { + "name": "create_tracker_account", + "docs": [ + "create tracker account" + ], + "discriminator": [ + 40, + 16, + 40, + 191, + 109, + 177, + 83, + 190 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "owner" + }, + { + "name": "asset_mint" + }, + { + "name": "tracker_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "asset_mint" + }, + { + "kind": "account", + "path": "owner" + } + ] + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, { "name": "detach_from_policy_account", "docs": [ @@ -248,6 +330,191 @@ "type": "string" } ] + }, + { + "name": "execute_transaction", + "docs": [ + "execute transfer hook" + ], + "discriminator": [ + 105, + 37, + 101, + 197, + 75, + 251, + 102, + 26 + ], + "accounts": [ + { + "name": "source_account", + "pda": { + "seeds": [ + { + "kind": "account", + "path": "owner_delegate" + }, + { + "kind": "const", + "value": [ + 6, + 221, + 246, + 225, + 238, + 117, + 143, + 222, + 24, + 66, + 93, + 188, + 228, + 108, + 205, + 218, + 182, + 26, + 252, + 77, + 131, + 185, + 13, + 39, + 254, + 189, + 249, + 40, + 216, + 161, + 139, + 252 + ] + }, + { + "kind": "account", + "path": "asset_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "asset_mint" + }, + { + "name": "destination_account" + }, + { + "name": "owner_delegate" + }, + { + "name": "extra_metas_account", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 101, + 120, + 116, + 114, + 97, + 45, + 97, + 99, + 99, + 111, + 117, + 110, + 116, + 45, + 109, + 101, + 116, + 97, + 115 + ] + }, + { + "kind": "account", + "path": "asset_mint" + } + ] + } + }, + { + "name": "policy_engine", + "address": "po1cPf1eyUJJPqULw4so3T4JU9pdFn83CDyuLEKFAau" + }, + { + "name": "policy_engine_account" + }, + { + "name": "identity_registry", + "address": "idtynCMYbdisCTv4FrCWPSQboZb1uM4TV2cPi79yxQf" + }, + { + "name": "identity_registry_account" + }, + { + "name": "identity_account", + "writable": true + }, + { + "name": "tracker_account", + "writable": true + }, + { + "name": "policy_account" + }, + { + "name": "instructions_program" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] } ], "accounts": [ @@ -276,6 +543,19 @@ 26, 45 ] + }, + { + "name": "TrackerAccount", + "discriminator": [ + 83, + 95, + 166, + 148, + 57, + 30, + 90, + 210 + ] } ], "errors": [ @@ -301,28 +581,63 @@ }, { "code": 6004, + "name": "IdentityLevelLimitExceeded", + "msg": "Identity level limit exceeded" + }, + { + "code": 6005, "name": "PolicyEngineFull", "msg": "Policy registry is full, cannot add more policies" }, { - "code": 6005, + "code": 6006, "name": "PolicyNotFound", "msg": "Policy not found" }, { - "code": 6006, + "code": 6007, "name": "IdentityFilterFailed", "msg": "Identity filter failed" }, { - "code": 6007, + "code": 6008, "name": "UnauthorizedSigner", "msg": "Unauthorized signer" }, { - "code": 6008, + "code": 6009, "name": "PolicyAlreadyExists", "msg": "Policy already exists" + }, + { + "code": 6010, + "name": "MaxBalanceExceeded", + "msg": "Max balance exceeded" + }, + { + "code": 6011, + "name": "InvalidCpiTransferAmount", + "msg": "Invalid CPI transfer amount" + }, + { + "code": 6012, + "name": "InvalidCpiTransferMint", + "msg": "Invalid CPI transfer mint" + }, + { + "code": 6013, + "name": "InvalidCpiTransferProgram", + "msg": "Invalid CPI transfer program" + }, + { + "code": 6014, + "name": "InvalidPdaPassedIn", + "msg": "Invalid PDA passed in" + }, + { + "code": 6015, + "name": "TransferHistoryFull", + "msg": "Transfer history full" } ], "types": [ @@ -514,6 +829,61 @@ "type": "i64" } ] + }, + { + "name": "MaxBalance", + "fields": [ + { + "name": "limit", + "type": "u64" + } + ] + } + ] + } + }, + { + "name": "TrackerAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "version", + "type": "u8" + }, + { + "name": "asset_mint", + "type": "pubkey" + }, + { + "name": "owner", + "type": "pubkey" + }, + { + "name": "transfers", + "type": { + "vec": { + "defined": { + "name": "Transfer" + } + } + } + } + ] + } + }, + { + "name": "Transfer", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "timestamp", + "type": "i64" } ] } diff --git a/clients/rwa-token-sdk/src/programs/types/AssetControllerTypes.ts b/clients/rwa-token-sdk/src/programs/types/AssetControllerTypes.ts index 22d38f5..00e20db 100644 --- a/clients/rwa-token-sdk/src/programs/types/AssetControllerTypes.ts +++ b/clients/rwa-token-sdk/src/programs/types/AssetControllerTypes.ts @@ -13,6 +13,99 @@ export type AssetController = { "description": "The Asset Controller Program (ACP) enables core asset management functionality for newly issued assets, including transfer controls and transaction privacy." }, "instructions": [ + { + "name": "burnTokens", + "docs": [ + "burn shares of the rwa asset" + ], + "discriminator": [ + 76, + 15, + 51, + 254, + 229, + 215, + 121, + 66 + ], + "accounts": [ + { + "name": "owner", + "signer": true + }, + { + "name": "assetMint", + "writable": true + }, + { + "name": "tokenAccount", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "owner" + }, + { + "kind": "account", + "path": "tokenProgram" + }, + { + "kind": "account", + "path": "assetMint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "tokenProgram", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, { "name": "closeMintAccount", "docs": [ @@ -208,11 +301,6 @@ export type AssetController = { { "name": "authority" }, - { - "name": "assetMint", - "writable": true, - "signer": true - }, { "name": "assetController", "writable": true, @@ -226,44 +314,113 @@ export type AssetController = { } }, { - "name": "extraMetasAccount", + "name": "assetMint", "writable": true, + "signer": true + }, + { + "name": "extraMetasAccount", + "writable": true + }, + { + "name": "policyEngineAccount", + "writable": true + }, + { + "name": "identityRegistryAccount", + "writable": true + }, + { + "name": "dataRegistryAccount", + "writable": true + }, + { + "name": "policyEngine", + "address": "po1cPf1eyUJJPqULw4so3T4JU9pdFn83CDyuLEKFAau" + }, + { + "name": "identityRegistry", + "address": "idtynCMYbdisCTv4FrCWPSQboZb1uM4TV2cPi79yxQf" + }, + { + "name": "dataRegistry", + "address": "dataeP5X1e7XsWN1ovDSEDP5cqaEUnKBmHE5iZhXPVw" + }, + { + "name": "systemProgram", + "address": "11111111111111111111111111111111" + }, + { + "name": "tokenProgram", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "name": "eventAuthority", "pda": { "seeds": [ { "kind": "const", "value": [ + 95, + 95, + 101, + 118, 101, - 120, + 110, 116, - 114, - 97, - 45, + 95, 97, - 99, - 99, - 111, 117, - 110, 116, - 45, - 109, - 101, + 104, + 111, + 114, + 105, 116, - 97, - 115 + 121 ] - }, - { - "kind": "account", - "path": "assetMint" } ] } }, { - "name": "systemProgram", - "address": "11111111111111111111111111111111" + "name": "program" + } + ], + "args": [ + { + "name": "args", + "type": { + "defined": { + "name": "createAssetControllerArgs" + } + } + } + ] + }, + { + "name": "disableMemoTransfer", + "docs": [ + "memo transfer disable" + ], + "discriminator": [ + 68, + 156, + 197, + 9, + 43, + 91, + 114, + 19 + ], + "accounts": [ + { + "name": "owner", + "signer": true + }, + { + "name": "tokenAccount", + "writable": true }, { "name": "tokenProgram", @@ -302,31 +459,22 @@ export type AssetController = { "name": "program" } ], - "args": [ - { - "name": "args", - "type": { - "defined": { - "name": "createAssetControllerArgs" - } - } - } - ] + "args": [] }, { - "name": "createTokenAccount", + "name": "enableMemoTransfer", "docs": [ - "create a token account" + "memo transfer enable" ], "discriminator": [ - 147, - 241, - 123, - 100, - 244, - 132, - 174, - 118 + 186, + 78, + 97, + 172, + 71, + 172, + 99, + 0 ], "accounts": [ { @@ -335,7 +483,8 @@ export type AssetController = { "signer": true }, { - "name": "owner" + "name": "owner", + "signer": true }, { "name": "assetMint" @@ -397,37 +546,6 @@ export type AssetController = { } } }, - { - "name": "trackerAccount", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "assetMint" - }, - { - "kind": "account", - "path": "owner" - } - ] - } - }, - { - "name": "assetController", - "pda": { - "seeds": [ - { - "kind": "account", - "path": "assetMint" - } - ] - } - }, - { - "name": "systemProgram", - "address": "11111111111111111111111111111111" - }, { "name": "tokenProgram", "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" @@ -436,6 +554,10 @@ export type AssetController = { "name": "associatedTokenProgram", "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" }, + { + "name": "systemProgram", + "address": "11111111111111111111111111111111" + }, { "name": "eventAuthority", "pda": { @@ -469,140 +591,105 @@ export type AssetController = { "name": "program" } ], - "args": [ - { - "name": "args", - "type": { - "defined": { - "name": "createTokenAccountArgs" - } - } - } - ] + "args": [] }, { - "name": "disableMemoTransfer", + "name": "freezeTokenAccount", "docs": [ - "memo transfer disable" + "freeze token account" ], "discriminator": [ - 68, - 156, - 197, - 9, - 43, - 91, - 114, - 19 + 138, + 168, + 178, + 109, + 205, + 224, + 209, + 93 ], "accounts": [ { - "name": "owner", + "name": "authority", "signer": true }, { - "name": "tokenAccount", + "name": "assetMint", "writable": true }, { - "name": "tokenProgram", - "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" - }, - { - "name": "eventAuthority", + "name": "assetController", "pda": { "seeds": [ { - "kind": "const", - "value": [ - 95, - 95, - 101, - 118, - 101, - 110, - 116, - 95, - 97, - 117, - 116, - 104, - 111, - 114, - 105, - 116, - 121 - ] + "kind": "account", + "path": "assetMint" } ] } }, { - "name": "program" + "name": "tokenAccount", + "writable": true + }, + { + "name": "tokenProgram", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" } ], "args": [] }, { - "name": "executeTransaction", + "name": "issueTokens", "docs": [ - "execute transfer hook" + "issue shares of the rwa asset" ], "discriminator": [ - 105, - 37, - 101, - 197, - 75, - 251, - 102, - 26 + 40, + 207, + 145, + 106, + 249, + 54, + 23, + 179 ], "accounts": [ { - "name": "sourceAccount", + "name": "authority", + "writable": true, + "signer": true + }, + { + "name": "assetMint", + "writable": true + }, + { + "name": "assetController", + "pda": { + "seeds": [ + { + "kind": "account", + "path": "assetMint" + } + ] + } + }, + { + "name": "to" + }, + { + "name": "tokenAccount", + "writable": true, "pda": { "seeds": [ { "kind": "account", - "path": "ownerDelegate" + "path": "to" }, { - "kind": "const", - "value": [ - 6, - 221, - 246, - 225, - 238, - 117, - 143, - 222, - 24, - 66, - 93, - 188, - 228, - 108, - 205, - 218, - 182, - 26, - 252, - 77, - 131, - 185, - 13, - 39, - 254, - 189, - 249, - 40, - 216, - 161, - 139, - 252 - ] + "kind": "account", + "path": "tokenProgram" }, { "kind": "account", @@ -645,80 +732,20 @@ export type AssetController = { 248, 89 ] - } - } - }, - { - "name": "assetMint" - }, - { - "name": "destinationAccount" - }, - { - "name": "ownerDelegate" - }, - { - "name": "extraMetasAccount", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 101, - 120, - 116, - 114, - 97, - 45, - 97, - 99, - 99, - 111, - 117, - 110, - 116, - 45, - 109, - 101, - 116, - 97, - 115 - ] - }, - { - "kind": "account", - "path": "assetMint" - } - ] + } } }, { - "name": "policyEngine", - "address": "po1cPf1eyUJJPqULw4so3T4JU9pdFn83CDyuLEKFAau" - }, - { - "name": "policyEngineAccount" - }, - { - "name": "identityRegistry", - "address": "idtynCMYbdisCTv4FrCWPSQboZb1uM4TV2cPi79yxQf" - }, - { - "name": "identityRegistryAccount" - }, - { - "name": "identityAccount", - "writable": true - }, - { - "name": "trackerAccount", - "writable": true + "name": "tokenProgram", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" }, { - "name": "policyAccount" + "name": "associatedTokenProgram", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" }, { - "name": "instructionsProgram" + "name": "systemProgram", + "address": "11111111111111111111111111111111" } ], "args": [ @@ -729,23 +756,24 @@ export type AssetController = { ] }, { - "name": "issueTokens", + "name": "revokeTokens", "docs": [ - "issue shares of the rwa asset" + "revoke shares of the rwa asset" ], "discriminator": [ - 40, - 207, - 145, - 106, - 249, - 54, - 23, - 179 + 215, + 42, + 15, + 134, + 173, + 80, + 33, + 21 ], "accounts": [ { "name": "authority", + "writable": true, "signer": true }, { @@ -753,13 +781,24 @@ export type AssetController = { "writable": true }, { - "name": "tokenAccount", + "name": "assetController", + "pda": { + "seeds": [ + { + "kind": "account", + "path": "assetMint" + } + ] + } + }, + { + "name": "authorityTokenAccount", "writable": true, "pda": { "seeds": [ { - "kind": "arg", - "path": "args.to" + "kind": "account", + "path": "assetController" }, { "kind": "account", @@ -809,22 +848,76 @@ export type AssetController = { } } }, + { + "name": "revokeTokenAccount", + "writable": true + }, { "name": "tokenProgram", "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "name": "associatedTokenProgram", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "systemProgram", + "address": "11111111111111111111111111111111" } ], "args": [ { - "name": "args", - "type": { - "defined": { - "name": "issueTokensArgs" - } - } + "name": "amount", + "type": "u64" } ] }, + { + "name": "thawTokenAccount", + "docs": [ + "thaw token account" + ], + "discriminator": [ + 199, + 172, + 96, + 93, + 244, + 252, + 137, + 171 + ], + "accounts": [ + { + "name": "authority", + "signer": true + }, + { + "name": "assetMint", + "writable": true + }, + { + "name": "assetController", + "pda": { + "seeds": [ + { + "kind": "account", + "path": "assetMint" + } + ] + } + }, + { + "name": "tokenAccount", + "writable": true + }, + { + "name": "tokenProgram", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + } + ], + "args": [] + }, { "name": "updateInterestBearingMintRate", "docs": [ @@ -996,99 +1089,6 @@ export type AssetController = { } } ] - }, - { - "name": "voidTokens", - "docs": [ - "void shares of the rwa asset" - ], - "discriminator": [ - 101, - 147, - 63, - 157, - 106, - 103, - 119, - 74 - ], - "accounts": [ - { - "name": "owner", - "signer": true - }, - { - "name": "assetMint", - "writable": true - }, - { - "name": "tokenAccount", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "owner" - }, - { - "kind": "account", - "path": "tokenProgram" - }, - { - "kind": "account", - "path": "assetMint" - } - ], - "program": { - "kind": "const", - "value": [ - 140, - 151, - 37, - 143, - 78, - 36, - 137, - 241, - 187, - 61, - 16, - 41, - 20, - 142, - 13, - 131, - 11, - 90, - 19, - 153, - 218, - 255, - 16, - 132, - 4, - 142, - 123, - 216, - 219, - 233, - 248, - 89 - ] - } - } - }, - { - "name": "tokenProgram", - "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" - } - ], - "args": [ - { - "name": "amount", - "type": "u64" - } - ] } ], "accounts": [ @@ -1104,19 +1104,6 @@ export type AssetController = { 52, 105 ] - }, - { - "name": "trackerAccount", - "discriminator": [ - 83, - 95, - 166, - 148, - 57, - 30, - 90, - 210 - ] } ], "events": [ @@ -1192,21 +1179,6 @@ export type AssetController = { "code": 6008, "name": "invalidPdaPassedIn", "msg": "Pda passed in for transfer is wrong" - }, - { - "code": 6009, - "name": "invalidCpiTransferProgram", - "msg": "Invalid cpi program in transfer" - }, - { - "code": 6010, - "name": "invalidCpiTransferAmount", - "msg": "Invalid cpi amount in transfer" - }, - { - "code": 6011, - "name": "invalidCpiTransferMint", - "msg": "Invalid cpi mint in transfer" } ], "types": [ @@ -1317,18 +1289,6 @@ export type AssetController = { ] } }, - { - "name": "createTokenAccountArgs", - "type": { - "kind": "struct", - "fields": [ - { - "name": "memoTransfer", - "type": "bool" - } - ] - } - }, { "name": "extensionMetadataEvent", "type": { @@ -1349,68 +1309,6 @@ export type AssetController = { ] } }, - { - "name": "issueTokensArgs", - "type": { - "kind": "struct", - "fields": [ - { - "name": "amount", - "type": "u64" - }, - { - "name": "to", - "type": "pubkey" - } - ] - } - }, - { - "name": "trackerAccount", - "type": { - "kind": "struct", - "fields": [ - { - "name": "version", - "type": "u8" - }, - { - "name": "assetMint", - "type": "pubkey" - }, - { - "name": "owner", - "type": "pubkey" - }, - { - "name": "transfers", - "type": { - "vec": { - "defined": { - "name": "transfer" - } - } - } - } - ] - } - }, - { - "name": "transfer", - "type": { - "kind": "struct", - "fields": [ - { - "name": "amount", - "type": "u64" - }, - { - "name": "timestamp", - "type": "i64" - } - ] - } - }, { "name": "updateAssetMetadataArgs", "type": { diff --git a/clients/rwa-token-sdk/src/programs/types/PolicyEngineTypes.ts b/clients/rwa-token-sdk/src/programs/types/PolicyEngineTypes.ts index dc8a034..098a58b 100644 --- a/clients/rwa-token-sdk/src/programs/types/PolicyEngineTypes.ts +++ b/clients/rwa-token-sdk/src/programs/types/PolicyEngineTypes.ts @@ -202,6 +202,88 @@ export type PolicyEngine = { } ] }, + { + "name": "createTrackerAccount", + "docs": [ + "create tracker account" + ], + "discriminator": [ + 40, + 16, + 40, + 191, + 109, + 177, + 83, + 190 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "owner" + }, + { + "name": "assetMint" + }, + { + "name": "trackerAccount", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "assetMint" + }, + { + "kind": "account", + "path": "owner" + } + ] + } + }, + { + "name": "systemProgram", + "address": "11111111111111111111111111111111" + }, + { + "name": "eventAuthority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, { "name": "detachFromPolicyAccount", "docs": [ @@ -254,6 +336,191 @@ export type PolicyEngine = { "type": "string" } ] + }, + { + "name": "executeTransaction", + "docs": [ + "execute transfer hook" + ], + "discriminator": [ + 105, + 37, + 101, + 197, + 75, + 251, + 102, + 26 + ], + "accounts": [ + { + "name": "sourceAccount", + "pda": { + "seeds": [ + { + "kind": "account", + "path": "ownerDelegate" + }, + { + "kind": "const", + "value": [ + 6, + 221, + 246, + 225, + 238, + 117, + 143, + 222, + 24, + 66, + 93, + 188, + 228, + 108, + 205, + 218, + 182, + 26, + 252, + 77, + 131, + 185, + 13, + 39, + 254, + 189, + 249, + 40, + 216, + 161, + 139, + 252 + ] + }, + { + "kind": "account", + "path": "assetMint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "assetMint" + }, + { + "name": "destinationAccount" + }, + { + "name": "ownerDelegate" + }, + { + "name": "extraMetasAccount", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 101, + 120, + 116, + 114, + 97, + 45, + 97, + 99, + 99, + 111, + 117, + 110, + 116, + 45, + 109, + 101, + 116, + 97, + 115 + ] + }, + { + "kind": "account", + "path": "assetMint" + } + ] + } + }, + { + "name": "policyEngine", + "address": "po1cPf1eyUJJPqULw4so3T4JU9pdFn83CDyuLEKFAau" + }, + { + "name": "policyEngineAccount" + }, + { + "name": "identityRegistry", + "address": "idtynCMYbdisCTv4FrCWPSQboZb1uM4TV2cPi79yxQf" + }, + { + "name": "identityRegistryAccount" + }, + { + "name": "identityAccount", + "writable": true + }, + { + "name": "trackerAccount", + "writable": true + }, + { + "name": "policyAccount" + }, + { + "name": "instructionsProgram" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] } ], "accounts": [ @@ -282,6 +549,19 @@ export type PolicyEngine = { 26, 45 ] + }, + { + "name": "trackerAccount", + "discriminator": [ + 83, + 95, + 166, + 148, + 57, + 30, + 90, + 210 + ] } ], "errors": [ @@ -307,28 +587,63 @@ export type PolicyEngine = { }, { "code": 6004, + "name": "identityLevelLimitExceeded", + "msg": "Identity level limit exceeded" + }, + { + "code": 6005, "name": "policyEngineFull", "msg": "Policy registry is full, cannot add more policies" }, { - "code": 6005, + "code": 6006, "name": "policyNotFound", "msg": "Policy not found" }, { - "code": 6006, + "code": 6007, "name": "identityFilterFailed", "msg": "Identity filter failed" }, { - "code": 6007, + "code": 6008, "name": "unauthorizedSigner", "msg": "Unauthorized signer" }, { - "code": 6008, + "code": 6009, "name": "policyAlreadyExists", "msg": "Policy already exists" + }, + { + "code": 6010, + "name": "maxBalanceExceeded", + "msg": "Max balance exceeded" + }, + { + "code": 6011, + "name": "invalidCpiTransferAmount", + "msg": "Invalid CPI transfer amount" + }, + { + "code": 6012, + "name": "invalidCpiTransferMint", + "msg": "Invalid CPI transfer mint" + }, + { + "code": 6013, + "name": "invalidCpiTransferProgram", + "msg": "Invalid CPI transfer program" + }, + { + "code": 6014, + "name": "invalidPdaPassedIn", + "msg": "Invalid PDA passed in" + }, + { + "code": 6015, + "name": "transferHistoryFull", + "msg": "Transfer history full" } ], "types": [ @@ -520,6 +835,61 @@ export type PolicyEngine = { "type": "i64" } ] + }, + { + "name": "maxBalance", + "fields": [ + { + "name": "limit", + "type": "u64" + } + ] + } + ] + } + }, + { + "name": "trackerAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "version", + "type": "u8" + }, + { + "name": "assetMint", + "type": "pubkey" + }, + { + "name": "owner", + "type": "pubkey" + }, + { + "name": "transfers", + "type": { + "vec": { + "defined": { + "name": "transfer" + } + } + } + } + ] + } + }, + { + "name": "transfer", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "timestamp", + "type": "i64" } ] } diff --git a/clients/rwa-token-sdk/tests/e2e.test.ts b/clients/rwa-token-sdk/tests/e2e.test.ts index 84ffe32..3d22955 100644 --- a/clients/rwa-token-sdk/tests/e2e.test.ts +++ b/clients/rwa-token-sdk/tests/e2e.test.ts @@ -4,13 +4,16 @@ import { CreateDataAccountArgs, DeleteDataAccountArgs, getDataAccountsWithFilter, + getFreezeTokenIx, getPolicyAccount, + getRevokeTokensIx, getSetupUserIxs, + getThawTokenIx, getTrackerAccount, type IssueTokenArgs, type TransferTokensArgs, type UpdateDataAccountArgs, - type VoidTokensArgs, + VoidTokensArgs, } from "../src"; import { setupTests } from "./setup"; import { @@ -50,7 +53,7 @@ describe("e2e tests", async () => { rwaClient = new RwaClient(config, new Wallet(setup.payerKp)); }); - test("initalize asset controller", async () => { + test("initialize asset controller", async () => { const setupAssetControllerArgs = { decimals, payer: setup.payer.toString(), @@ -65,21 +68,40 @@ describe("e2e tests", async () => { ); mint = setupIx.signers[0].publicKey.toString(); const setupUserIxs = await getSetupUserIxs({ + assetMint: mint, + payer: setup.payer.toString(), + owner: setup.user1.toString(), + signer: setup.authority.toString(), + level: 1, + }, rwaClient.provider); + const setupUser2Ixs = await getSetupUserIxs({ + assetMint: mint, + payer: setup.payer.toString(), + owner: setup.user2.toString(), + signer: setup.authority.toString(), + level: 1, + }, rwaClient.provider); + const setupUser3Ixs = await getSetupUserIxs({ assetMint: mint, payer: setup.payer.toString(), owner: setup.authority.toString(), signer: setup.authority.toString(), - level: 255 + level: 255, }, rwaClient.provider); const txnId = await sendAndConfirmTransaction( rwaClient.provider.connection, new Transaction().add(...setupIx.ixs).add(...setupUserIxs.ixs), - [setup.payerKp, ...setupIx.signers, ...setupUserIxs.signers] + [setup.payerKp, setup.authorityKp, ...setupIx.signers, ...setupUserIxs.signers] + ); + await sendAndConfirmTransaction( + rwaClient.provider.connection, + new Transaction().add(...setupUser2Ixs.ixs).add(...setupUser3Ixs.ixs), + [setup.payerKp, setup.authorityKp, ...setupUser2Ixs.signers, ...setupUser3Ixs.signers] ); expect(txnId).toBeTruthy(); const trackerAccount = await getTrackerAccount( mint, - setup.authority.toString(), + setup.user1.toString(), rwaClient.provider ); expect(trackerAccount).toBeTruthy(); @@ -100,7 +122,7 @@ describe("e2e tests", async () => { const txnId = await sendAndConfirmTransaction( rwaClient.provider.connection, new Transaction().add(updateIx), - [setup.payerKp] + [setup.payerKp, setup.authorityKp] ); expect(txnId).toBeTruthy(); }); @@ -124,10 +146,6 @@ describe("e2e tests", async () => { ); expect(txnId).toBeTruthy(); dataAccount = createDataAccountIx.signers[0].publicKey.toString(); - console.log( - "data account: ", - createDataAccountIx.signers[0].publicKey.toString() - ); }); test("create identity approval policy", async () => { @@ -164,7 +182,7 @@ describe("e2e tests", async () => { }, policyType: { transactionAmountLimit: { - limit: new BN(100), + limit: new BN(10000), }, }, }; @@ -233,14 +251,15 @@ describe("e2e tests", async () => { const issueArgs: IssueTokenArgs = { authority: setup.authority.toString(), payer: setup.payer.toString(), - owner: setup.authority.toString(), + owner: setup.user1.toString(), assetMint: mint, amount: 1000000, }; const issueIx = await rwaClient.assetController.issueTokenIxns(issueArgs); + const issue2Ix = await rwaClient.assetController.issueTokenIxns({...issueArgs, owner: setup.authority.toString() }); const txnId = await sendAndConfirmTransaction( rwaClient.provider.connection, - new Transaction().add(issueIx), + new Transaction().add(...issueIx).add(...issue2Ix), [setup.payerKp, setup.authorityKp] ); expect(txnId).toBeTruthy(); @@ -250,19 +269,62 @@ describe("e2e tests", async () => { const voidArgs: VoidTokensArgs = { payer: setup.payer.toString(), amount: 100, - owner: setup.authority.toString(), + owner: setup.user1.toString(), assetMint: mint, - authority: setup.authority.toString(), + authority: setup.user1.toString(), }; const voidIx = await rwaClient.assetController.voidTokenIxns(voidArgs); const txnId = await sendAndConfirmTransaction( rwaClient.provider.connection, new Transaction().add(voidIx), + [setup.payerKp, setup.user1Kp] + ); + expect(txnId).toBeTruthy(); + }); + + test("revoke tokens", async () => { + const revokeIx = await getRevokeTokensIx({ + owner: setup.user1.toString(), + assetMint: mint, + amount: 100, + authority: setup.authority.toString(), + }, rwaClient.provider); + const txnId = await sendAndConfirmTransaction( + rwaClient.provider.connection, + new Transaction().add(...revokeIx), [setup.payerKp, setup.authorityKp] ); expect(txnId).toBeTruthy(); }); + test("freeze and thaw token account", async () => { + const freezeIx = await getFreezeTokenIx({ + authority: setup.authority.toString(), + payer: setup.payer.toString(), + owner: setup.user1.toString(), + assetMint: mint, + }, rwaClient.provider); + const txnId = await sendAndConfirmTransaction( + rwaClient.provider.connection, + new Transaction().add(freezeIx), + [setup.payerKp, setup.authorityKp] + ); + expect(txnId).toBeTruthy(); + + const thawIx = await getThawTokenIx({ + authority: setup.authority.toString(), + payer: setup.payer.toString(), + owner: setup.user1.toString(), + assetMint: mint, + }, rwaClient.provider); + const txnId2 = await sendAndConfirmTransaction( + rwaClient.provider.connection, + new Transaction().add(thawIx), + [setup.payerKp, setup.authorityKp] + ); + expect(txnId2).toBeTruthy(); + }); + test("update data account", async () => { const updateDataAccountArgs: UpdateDataAccountArgs = { dataAccount, @@ -270,7 +332,6 @@ describe("e2e tests", async () => { uri: "newUri", type: { tax: {} }, payer: setup.payer.toString(), - owner: setup.authority.toString(), assetMint: mint, authority: setup.authority.toString(), signer: setup.authority.toString(), @@ -288,16 +349,14 @@ describe("e2e tests", async () => { const deleteDataAccountArgs: DeleteDataAccountArgs = { dataAccount, payer: setup.payer.toString(), - owner: setup.authority.toString(), assetMint: mint, - authority: setup.authority.toString(), signer: setup.authority.toString(), }; const updateDataIx = await rwaClient.dataRegistry.deleteAssetsDataAccountInfoIxns(deleteDataAccountArgs); const txnId = await sendAndConfirmTransaction( rwaClient.provider.connection, new Transaction().add(updateDataIx), - [setup.payerKp, setup.authorityKp] + [setup.authorityKp] ); expect(txnId).toBeTruthy(); expect(await getDataAccountsWithFilter({assetMint: mint}, rwaClient.provider)).toHaveLength(0); @@ -305,20 +364,19 @@ describe("e2e tests", async () => { test("transfer tokens", async () => { const transferArgs: TransferTokensArgs = { - authority: setup.authority.toString(), - payer: setup.payer.toString(), - from: setup.authority.toString(), - to: setup.authority.toString(), + from: setup.user1.toString(), + to: setup.user2.toString(), assetMint: mint, amount: 2000, decimals, + createTa: true, }; const transferIxs = await rwaClient.assetController.transfer(transferArgs); const txnId = await sendAndConfirmTransaction( rwaClient.provider.connection, new Transaction().add(...transferIxs), - [setup.payerKp] + [setup.user1Kp] ); expect(txnId).toBeTruthy(); }); diff --git a/clients/rwa-token-sdk/tests/extensions.test.ts b/clients/rwa-token-sdk/tests/extensions.test.ts index 67a2d55..769ab32 100644 --- a/clients/rwa-token-sdk/tests/extensions.test.ts +++ b/clients/rwa-token-sdk/tests/extensions.test.ts @@ -2,6 +2,7 @@ import { Wallet } from "@coral-xyz/anchor"; import { getCloseMintIx, getDisableMemoTransferIx, + getEnableMemoTransferIx, getSetupUserIxs, getUpdateInterestBearingMintRateIx, } from "../src"; @@ -66,14 +67,13 @@ describe("extension tests", async () => { owner: setup.authority.toString(), signer: setup.authority.toString(), level: 255, - memoTransfer: true, }, rwaClient.provider ); const txnId = await sendAndConfirmTransaction( rwaClient.provider.connection, new Transaction().add(...setupIx.ixs).add(...setupUser.ixs), - [setup.payerKp, ...setupIx.signers, ...setupUser.signers] + [setup.payerKp, setup.authorityKp, ...setupIx.signers, ...setupUser.signers] ); mint = setupIx.signers[0].publicKey.toString(); expect(txnId).toBeTruthy(); @@ -87,13 +87,29 @@ describe("extension tests", async () => { mintData, ); expect(interestBearingMintConfig?.currentRate).toEqual(100); + }); + + test("enable memo transfer", async () => { + const enableMemoTransferIx = await getEnableMemoTransferIx( + { + owner: setup.authority.toString(), + tokenAccount: getAssociatedTokenAddressSync(new PublicKey(mint), new PublicKey(setup.authority.toString()), undefined, TOKEN_2022_PROGRAM_ID).toString(), + assetMint: mint, + }, + rwaClient.provider + ); + const txnId = await sendAndConfirmTransaction( + rwaClient.provider.connection, + new Transaction().add(enableMemoTransferIx), + [setup.payerKp, setup.authorityKp] + ); + expect(txnId).toBeTruthy(); const tokenAccount = await getAccount( rwaClient.provider.connection, getAssociatedTokenAddressSync(new PublicKey(mint), new PublicKey(setup.authority.toString()), undefined, TOKEN_2022_PROGRAM_ID), undefined, TOKEN_2022_PROGRAM_ID ); - // Get Interest Config for Mint Account const memoTransfer = getMemoTransfer( tokenAccount, ); @@ -129,11 +145,13 @@ describe("extension tests", async () => { expect(interestBearingMintConfig?.currentRate).toEqual(200); }); + test("disable transfer memo", async () => { const updateIx = await getDisableMemoTransferIx( { owner: setup.authority.toString(), tokenAccount: getAssociatedTokenAddressSync(new PublicKey(mint), new PublicKey(setup.authority.toString()), undefined, TOKEN_2022_PROGRAM_ID).toString(), + assetMint: mint, }, rwaClient.provider ); diff --git a/clients/rwa-token-sdk/tests/policies.test.ts b/clients/rwa-token-sdk/tests/policies.test.ts index b7900ec..b0b6045 100644 --- a/clients/rwa-token-sdk/tests/policies.test.ts +++ b/clients/rwa-token-sdk/tests/policies.test.ts @@ -47,7 +47,7 @@ describe("test policy setup", async () => { const setupAssetController = await rwaClient.assetController.setupNewRegistry( createAssetControllerArgs ); - const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...setupAssetController.ixs), [setup.payerKp, ...setupAssetController.signers]); + const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...setupAssetController.ixs), [setup.payerKp, setup.authorityKp, ...setupAssetController.signers]); mint = setupAssetController.signers[0].publicKey.toString(); expect(txnId).toBeTruthy(); }); @@ -65,7 +65,7 @@ describe("test policy setup", async () => { identityApproval: {}, }, }); - const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...attachPolicy.ixs), [setup.payerKp, ...attachPolicy.signers]); + const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...attachPolicy.ixs), [setup.payerKp, setup.authorityKp, ...attachPolicy.signers]); expect(txnId).toBeTruthy(); }); @@ -84,7 +84,7 @@ describe("test policy setup", async () => { }, }, }); - const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...attachPolicy.ixs), [setup.payerKp, ...attachPolicy.signers]); + const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...attachPolicy.ixs), [setup.payerKp, setup.authorityKp, ...attachPolicy.signers]); expect(txnId).toBeTruthy(); }); @@ -103,7 +103,7 @@ describe("test policy setup", async () => { }, }, }); - const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...attachPolicy.ixs), [setup.payerKp, ...attachPolicy.signers]); + const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...attachPolicy.ixs), [setup.payerKp, setup.authorityKp, ...attachPolicy.signers]); expect(txnId).toBeTruthy(); }); @@ -118,12 +118,12 @@ describe("test policy setup", async () => { }, policyType: { transactionAmountVelocity: { - limit: new BN(199), - timeframe: new BN(60), + limit: new BN(20), + timeframe: new BN(3000), }, }, }); - const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...attachPolicy.ixs), [setup.payerKp, ...attachPolicy.signers]); + const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...attachPolicy.ixs), [setup.payerKp, setup.authorityKp, ...attachPolicy.signers]); expect(txnId).toBeTruthy(); }); @@ -143,7 +143,7 @@ describe("test policy setup", async () => { }, }, }); - const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...attachPolicy.ixs), [setup.payerKp, ...attachPolicy.signers]); + const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...attachPolicy.ixs), [setup.payerKp, setup.authorityKp, ...attachPolicy.signers]); expect(txnId).toBeTruthy(); }); @@ -163,7 +163,7 @@ describe("test policy setup", async () => { }, }, }); - const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...attachPolicy.ixs), [setup.payerKp, ...attachPolicy.signers]); + const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...attachPolicy.ixs), [setup.payerKp, setup.authorityKp, ...attachPolicy.signers]); expect(txnId).toBeTruthy(); const policyAccount = await getPolicyEngineProgram(setup.provider).account.policyAccount.fetch(getPolicyAccountPda(mint)); expect(policyAccount.policies.length).toBe(6); @@ -177,7 +177,7 @@ describe("test policy setup", async () => { level: 1, signer: setup.authorityKp.publicKey.toString() }); - const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...setupUser.ixs), [setup.payerKp, ...setupUser.signers]); + const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...setupUser.ixs), [setup.payerKp, setup.authorityKp, ...setupUser.signers]); expect(txnId).toBeTruthy(); }); @@ -189,7 +189,7 @@ describe("test policy setup", async () => { level: 2, signer: setup.authorityKp.publicKey.toString() }); - const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...setupUser.ixs), [setup.payerKp, ...setupUser.signers]); + const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...setupUser.ixs), [setup.payerKp, setup.authorityKp, ...setupUser.signers]); expect(txnId).toBeTruthy(); }); @@ -201,7 +201,7 @@ describe("test policy setup", async () => { level: 255, // Skips all policies signer: setup.authorityKp.publicKey.toString() }); - const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...setupUser.ixs), [setup.payerKp, ...setupUser.signers]); + const txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...setupUser.ixs), [setup.payerKp, setup.authorityKp, ...setupUser.signers]); expect(txnId).toBeTruthy(); }); @@ -213,7 +213,7 @@ describe("test policy setup", async () => { assetMint: mint, amount: 1000000, }); - let txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(issueTokens), [setup.payerKp]); + let txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...issueTokens), [setup.payerKp, setup.authorityKp]); expect(txnId).toBeTruthy(); issueTokens = await rwaClient.assetController.issueTokenIxns({ authority: setup.authority.toString(), @@ -222,7 +222,7 @@ describe("test policy setup", async () => { assetMint: mint, amount: 1000000, }); - txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(issueTokens), [setup.payerKp]); + txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...issueTokens), [setup.payerKp, setup.authorityKp]); expect(txnId).toBeTruthy(); issueTokens = await rwaClient.assetController.issueTokenIxns({ authority: setup.authority.toString(), @@ -231,14 +231,12 @@ describe("test policy setup", async () => { assetMint: mint, amount: 1000000, }); - txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(issueTokens), [setup.payerKp]); + txnId = await sendAndConfirmTransaction(setup.provider.connection, new Transaction().add(...issueTokens), [setup.payerKp, setup.authorityKp]); expect(txnId).toBeTruthy(); }); test("transfer 1000 tokens from user1, user2 and user3. fail for user1, success for others", async () => { let transferTokensIxs = await getTransferTokensIxs({ - authority: setup.authority.toString(), - payer: setup.payer.toString(), from: setup.user2.toString(), to: setup.user1.toString(), assetMint: mint, @@ -248,11 +246,9 @@ describe("test policy setup", async () => { void expect(sendAndConfirmTransaction( setup.provider.connection, new Transaction().add(...transferTokensIxs), - [setup.payerKp, setup.user2Kp], + [setup.user2Kp], )).rejects.toThrowError(); transferTokensIxs = await getTransferTokensIxs({ - authority: setup.authority.toString(), - payer: setup.payer.toString(), from: setup.user3.toString(), to: setup.user2.toString(), assetMint: mint, @@ -262,12 +258,10 @@ describe("test policy setup", async () => { let txnId = await sendAndConfirmTransaction( setup.provider.connection, new Transaction().add(...transferTokensIxs), - [setup.payerKp, setup.user3Kp], + [setup.user3Kp], ); expect(txnId).toBeTruthy(); transferTokensIxs = await getTransferTokensIxs({ - authority: setup.authority.toString(), - payer: setup.payer.toString(), from: setup.user1.toString(), to: setup.user3.toString(), assetMint: mint, @@ -277,17 +271,15 @@ describe("test policy setup", async () => { txnId = await sendAndConfirmTransaction( setup.provider.connection, new Transaction().add(...transferTokensIxs), - [setup.payerKp, setup.user1Kp], + [setup.user1Kp], ); expect(txnId).toBeTruthy(); }); - test("transfer 10 tokens 3 times from user1, fail 3rd time", async () => { + test("transfer 10 tokens 3 times to user1, fail 3rd time", async () => { let transferTokensIxs = await getTransferTokensIxs({ - authority: setup.authority.toString(), - payer: setup.payer.toString(), - from: setup.user1.toString(), - to: setup.user2.toString(), + from: setup.user2.toString(), + to: setup.user1.toString(), assetMint: mint, amount: 10, decimals, @@ -295,14 +287,12 @@ describe("test policy setup", async () => { let txnId = await sendAndConfirmTransaction( setup.provider.connection, new Transaction().add(...transferTokensIxs), - [setup.payerKp, setup.user1Kp], + [setup.user2Kp], ); expect(txnId).toBeTruthy(); transferTokensIxs = await getTransferTokensIxs({ - authority: setup.authority.toString(), - payer: setup.payer.toString(), - from: setup.user1.toString(), - to: setup.user2.toString(), + from: setup.user2.toString(), + to: setup.user1.toString(), assetMint: mint, amount: 10, decimals, @@ -310,22 +300,20 @@ describe("test policy setup", async () => { txnId = await sendAndConfirmTransaction( setup.provider.connection, new Transaction().add(...transferTokensIxs), - [setup.payerKp, setup.user1Kp], + [setup.user2Kp], ); expect(txnId).toBeTruthy(); transferTokensIxs = await getTransferTokensIxs({ - authority: setup.authority.toString(), - payer: setup.payer.toString(), - from: setup.user1.toString(), - to: setup.user2.toString(), + from: setup.user2.toString(), + to: setup.user1.toString(), assetMint: mint, - amount: 10, + amount: 1000, decimals, }, rwaClient.provider); void expect(sendAndConfirmTransaction( setup.provider.connection, new Transaction().add(...transferTokensIxs), - [setup.payerKp, setup.user1Kp], + [setup.user2Kp], )).rejects.toThrowError(); }); }); \ No newline at end of file diff --git a/clients/rwa-token-sdk/tests/setup.ts b/clients/rwa-token-sdk/tests/setup.ts index 365ad66..c9de1ad 100644 --- a/clients/rwa-token-sdk/tests/setup.ts +++ b/clients/rwa-token-sdk/tests/setup.ts @@ -4,8 +4,8 @@ import "dotenv/config"; export async function setupTests() { const payerKp = new Keypair(); - const authorityKp = payerKp; - const delegateKp = authorityKp; + const authorityKp = new Keypair(); + const delegateKp = new Keypair(); const user1Kp = new Keypair(); const user2Kp = new Keypair(); const user3Kp = new Keypair(); diff --git a/clients/rwa-token-sdk/tests/tracker.test.ts b/clients/rwa-token-sdk/tests/tracker.test.ts index c243339..2b68b69 100644 --- a/clients/rwa-token-sdk/tests/tracker.test.ts +++ b/clients/rwa-token-sdk/tests/tracker.test.ts @@ -58,7 +58,7 @@ describe("test suite to test tracker account is being updated correctly on trans const txnId = await sendAndConfirmTransaction( rwaClient.provider.connection, new Transaction().add(...setupIx.ixs), - [setup.payerKp, ...setupIx.signers] + [setup.payerKp, setup.authorityKp, ...setupIx.signers] ); mint = setupIx.signers[0].publicKey.toString(); expect(txnId).toBeTruthy(); @@ -97,21 +97,6 @@ describe("test suite to test tracker account is being updated correctly on trans [setup.payerKp, setup.authorityKp] ); expect(txnId2).toBeTruthy(); - const trackerAccount1 = await getTrackerAccount( - mint, - setup.user1.toString(), - rwaClient.provider - ); - expect(trackerAccount1).toBeTruthy(); - expect(trackerAccount1!.assetMint.toString()).toBe(mint); - expect(trackerAccount1!.owner.toString()).toBe(setup.user1.toString()); - const trackerAccount2 = await getTrackerAccount( - mint, - setup.user2.toString(), - rwaClient.provider - ); - expect(trackerAccount2).toBeTruthy(); - expect(trackerAccount2!.assetMint.toString()).toBe(mint); }); test("issue tokens", async () => { @@ -125,7 +110,7 @@ describe("test suite to test tracker account is being updated correctly on trans const issueIx = await rwaClient.assetController.issueTokenIxns(issueArgs); const txnId = await sendAndConfirmTransaction( rwaClient.provider.connection, - new Transaction().add(issueIx), + new Transaction().add(...issueIx), [setup.payerKp, setup.authorityKp] ); expect(txnId).toBeTruthy(); @@ -133,7 +118,6 @@ describe("test suite to test tracker account is being updated correctly on trans test("transfer tokens", async () => { const transferArgs: TransferTokensArgs = { - payer: setup.payer.toString(), from: setup.user1.toString(), to: setup.user2.toString(), assetMint: mint, @@ -146,7 +130,7 @@ describe("test suite to test tracker account is being updated correctly on trans const txnId = await sendAndConfirmTransaction( rwaClient.provider.connection, new Transaction().add(...transferIxs), - [setup.payerKp, setup.user1Kp] + [setup.user1Kp] ); expect(txnId).toBeTruthy(); const trackerAccount = await getTrackerAccount( @@ -183,7 +167,6 @@ describe("test suite to test tracker account is being updated correctly on trans test("do 25 transfers, fail for the 26th time because transfer history is full", async () => { for(let i = 0; i < 25; i++) { const transferArgs: TransferTokensArgs = { - payer: setup.payer.toString(), from: setup.user1.toString(), to: setup.user2.toString(), assetMint: mint, @@ -199,7 +182,7 @@ describe("test suite to test tracker account is being updated correctly on trans const txnId = await sendAndConfirmTransaction( rwaClient.provider.connection, new Transaction().add(...transferIxs), - [setup.payerKp, setup.user1Kp], + [setup.user1Kp], { commitment, } @@ -216,7 +199,6 @@ describe("test suite to test tracker account is being updated correctly on trans } } const transferArgs: TransferTokensArgs = { - payer: setup.payer.toString(), from: setup.user1.toString(), to: setup.user2.toString(), assetMint: mint, @@ -228,8 +210,8 @@ describe("test suite to test tracker account is being updated correctly on trans expect(sendAndConfirmTransaction( rwaClient.provider.connection, new Transaction().add(...transferIxs), - [setup.payerKp, setup.user1Kp] - )).rejects.toThrowError(/failed \(\{"err":\{"InstructionError":\[0,\{"Custom":6006\}\]\}\}\)/); + [setup.user1Kp] + )).rejects.toThrowError(/failed \(\{"err":\{"InstructionError":\[0,\{"Custom":6015\}\]\}\}\)/); }); }); diff --git a/programs/Cargo.lock b/programs/Cargo.lock index eb4e6cf..6f1f645 100644 --- a/programs/Cargo.lock +++ b/programs/Cargo.lock @@ -88,7 +88,7 @@ dependencies = [ [[package]] name = "anchor-attribute-access-control" version = "0.30.1" -source = "git+https://github.com/bridgesplit/anchor#c46c1bf1b12163d5d00ed9edd7e72e1a117362bb" +source = "git+https://github.com/bridgesplit/anchor#b5eb8650733a4e057dbb115192c3f399ce121b4d" dependencies = [ "anchor-syn", "proc-macro2", @@ -99,7 +99,7 @@ dependencies = [ [[package]] name = "anchor-attribute-account" version = "0.30.1" -source = "git+https://github.com/bridgesplit/anchor#c46c1bf1b12163d5d00ed9edd7e72e1a117362bb" +source = "git+https://github.com/bridgesplit/anchor#b5eb8650733a4e057dbb115192c3f399ce121b4d" dependencies = [ "anchor-syn", "bs58 0.5.1", @@ -111,7 +111,7 @@ dependencies = [ [[package]] name = "anchor-attribute-constant" version = "0.30.1" -source = "git+https://github.com/bridgesplit/anchor#c46c1bf1b12163d5d00ed9edd7e72e1a117362bb" +source = "git+https://github.com/bridgesplit/anchor#b5eb8650733a4e057dbb115192c3f399ce121b4d" dependencies = [ "anchor-syn", "quote", @@ -121,7 +121,7 @@ dependencies = [ [[package]] name = "anchor-attribute-error" version = "0.30.1" -source = "git+https://github.com/bridgesplit/anchor#c46c1bf1b12163d5d00ed9edd7e72e1a117362bb" +source = "git+https://github.com/bridgesplit/anchor#b5eb8650733a4e057dbb115192c3f399ce121b4d" dependencies = [ "anchor-syn", "quote", @@ -131,7 +131,7 @@ dependencies = [ [[package]] name = "anchor-attribute-event" version = "0.30.1" -source = "git+https://github.com/bridgesplit/anchor#c46c1bf1b12163d5d00ed9edd7e72e1a117362bb" +source = "git+https://github.com/bridgesplit/anchor#b5eb8650733a4e057dbb115192c3f399ce121b4d" dependencies = [ "anchor-syn", "proc-macro2", @@ -142,7 +142,7 @@ dependencies = [ [[package]] name = "anchor-attribute-program" version = "0.30.1" -source = "git+https://github.com/bridgesplit/anchor#c46c1bf1b12163d5d00ed9edd7e72e1a117362bb" +source = "git+https://github.com/bridgesplit/anchor#b5eb8650733a4e057dbb115192c3f399ce121b4d" dependencies = [ "anchor-lang-idl", "anchor-syn", @@ -158,7 +158,7 @@ dependencies = [ [[package]] name = "anchor-derive-accounts" version = "0.30.1" -source = "git+https://github.com/bridgesplit/anchor#c46c1bf1b12163d5d00ed9edd7e72e1a117362bb" +source = "git+https://github.com/bridgesplit/anchor#b5eb8650733a4e057dbb115192c3f399ce121b4d" dependencies = [ "anchor-syn", "quote", @@ -168,7 +168,7 @@ dependencies = [ [[package]] name = "anchor-derive-serde" version = "0.30.1" -source = "git+https://github.com/bridgesplit/anchor#c46c1bf1b12163d5d00ed9edd7e72e1a117362bb" +source = "git+https://github.com/bridgesplit/anchor#b5eb8650733a4e057dbb115192c3f399ce121b4d" dependencies = [ "anchor-syn", "borsh-derive-internal 0.10.3", @@ -180,7 +180,7 @@ dependencies = [ [[package]] name = "anchor-derive-space" version = "0.30.1" -source = "git+https://github.com/bridgesplit/anchor#c46c1bf1b12163d5d00ed9edd7e72e1a117362bb" +source = "git+https://github.com/bridgesplit/anchor#b5eb8650733a4e057dbb115192c3f399ce121b4d" dependencies = [ "proc-macro2", "quote", @@ -190,7 +190,7 @@ dependencies = [ [[package]] name = "anchor-lang" version = "0.30.1" -source = "git+https://github.com/bridgesplit/anchor#c46c1bf1b12163d5d00ed9edd7e72e1a117362bb" +source = "git+https://github.com/bridgesplit/anchor#b5eb8650733a4e057dbb115192c3f399ce121b4d" dependencies = [ "anchor-attribute-access-control", "anchor-attribute-account", @@ -214,7 +214,7 @@ dependencies = [ [[package]] name = "anchor-lang-idl" version = "0.1.1" -source = "git+https://github.com/bridgesplit/anchor#c46c1bf1b12163d5d00ed9edd7e72e1a117362bb" +source = "git+https://github.com/bridgesplit/anchor#b5eb8650733a4e057dbb115192c3f399ce121b4d" dependencies = [ "anchor-lang-idl-spec", "anyhow", @@ -228,7 +228,7 @@ dependencies = [ [[package]] name = "anchor-lang-idl-spec" version = "0.1.0" -source = "git+https://github.com/bridgesplit/anchor#c46c1bf1b12163d5d00ed9edd7e72e1a117362bb" +source = "git+https://github.com/bridgesplit/anchor#b5eb8650733a4e057dbb115192c3f399ce121b4d" dependencies = [ "anyhow", "serde", @@ -237,7 +237,7 @@ dependencies = [ [[package]] name = "anchor-spl" version = "0.30.1" -source = "git+https://github.com/bridgesplit/anchor#c46c1bf1b12163d5d00ed9edd7e72e1a117362bb" +source = "git+https://github.com/bridgesplit/anchor#b5eb8650733a4e057dbb115192c3f399ce121b4d" dependencies = [ "anchor-lang", "spl-associated-token-account", @@ -251,7 +251,7 @@ dependencies = [ [[package]] name = "anchor-syn" version = "0.30.1" -source = "git+https://github.com/bridgesplit/anchor#c46c1bf1b12163d5d00ed9edd7e72e1a117362bb" +source = "git+https://github.com/bridgesplit/anchor#b5eb8650733a4e057dbb115192c3f399ce121b4d" dependencies = [ "anyhow", "bs58 0.5.1", @@ -428,11 +428,11 @@ version = "0.0.1" dependencies = [ "anchor-lang", "anchor-spl", + "data_registry", "identity_registry", "policy_engine", "rwa_utils", "serde_json", - "spl-tlv-account-resolution", "spl-token-2022", "spl-transfer-hook-interface", ] @@ -1610,10 +1610,13 @@ version = "0.0.1" dependencies = [ "anchor-lang", "anchor-spl", + "identity_registry", "num_enum", "rwa_utils", "serde", "sha256", + "spl-tlv-account-resolution", + "spl-transfer-hook-interface", ] [[package]] diff --git a/programs/asset_controller/Cargo.toml b/programs/asset_controller/Cargo.toml index d168fc5..995b8f4 100644 --- a/programs/asset_controller/Cargo.toml +++ b/programs/asset_controller/Cargo.toml @@ -18,12 +18,12 @@ default = [] idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"] [dependencies] -anchor-lang = { git = "https://github.com/bridgesplit/anchor", features = ["interface-instructions", "init-if-needed", "event-cpi"] } +anchor-lang = { git = "https://github.com/bridgesplit/anchor", features = ["init-if-needed", "event-cpi"] } anchor-spl = { git = "https://github.com/bridgesplit/anchor", features = ["token_2022_extensions", "token_2022"] } -spl-transfer-hook-interface = { version = "0.6.3" } -spl-tlv-account-resolution = "0.6.3" policy_engine = { path = "../policy_engine", features = ["cpi"] } identity_registry = { path = "../identity_registry", features = ["cpi"] } +data_registry = { path = "../data_registry", features = ["cpi"] } rwa_utils = { path = "../rwa_utils" } serde_json = "1.0" -spl-token-2022 = { version = "3.0.2", features = ["serde-traits"]} \ No newline at end of file +spl-token-2022 = { version = "3.0.2", features = ["serde-traits"]} +spl-transfer-hook-interface = { version = "0.6.3" } diff --git a/programs/asset_controller/src/error.rs b/programs/asset_controller/src/error.rs index cbc5c10..e34ba54 100644 --- a/programs/asset_controller/src/error.rs +++ b/programs/asset_controller/src/error.rs @@ -20,10 +20,4 @@ pub enum AssetControllerErrors { Unauthorized, #[msg("Pda passed in for transfer is wrong")] InvalidPdaPassedIn, - #[msg("Invalid cpi program in transfer")] - InvalidCpiTransferProgram, - #[msg("Invalid cpi amount in transfer")] - InvalidCpiTransferAmount, - #[msg("Invalid cpi mint in transfer")] - InvalidCpiTransferMint, } diff --git a/programs/asset_controller/src/instructions/account/freeze.rs b/programs/asset_controller/src/instructions/account/freeze.rs new file mode 100644 index 0000000..6709024 --- /dev/null +++ b/programs/asset_controller/src/instructions/account/freeze.rs @@ -0,0 +1,52 @@ +use crate::state::*; +use anchor_lang::prelude::*; +use anchor_spl::{ + token_2022::{freeze_account, FreezeAccount}, + token_interface::{Mint, Token2022, TokenAccount}, +}; +use rwa_utils::get_bump_in_seed_form; + +#[derive(Accounts)] +#[instruction()] +pub struct FreezeTokenAccount<'info> { + #[account()] + pub authority: Signer<'info>, + #[account(mut)] + pub asset_mint: Box>, + #[account( + seeds = [asset_mint.key().as_ref()], + bump, + constraint = asset_controller.authority == authority.key() + )] + pub asset_controller: Box>, + #[account(mut)] + pub token_account: Box>, + pub token_program: Program<'info, Token2022>, +} + +impl<'info> FreezeTokenAccount<'info> { + fn freeze_tokens(&self, signer_seeds: &[&[&[u8]]]) -> Result<()> { + let accounts = FreezeAccount { + mint: self.asset_mint.to_account_info(), + authority: self.asset_controller.to_account_info(), + account: self.token_account.to_account_info(), + }; + let cpi_ctx = CpiContext::new_with_signer( + self.token_program.to_account_info(), + accounts, + signer_seeds, + ); + freeze_account(cpi_ctx)?; + Ok(()) + } +} + +pub fn handler(ctx: Context) -> Result<()> { + let asset_mint = ctx.accounts.asset_mint.key(); + let signer_seeds = [ + asset_mint.as_ref(), + &get_bump_in_seed_form(&ctx.bumps.asset_controller), + ]; + ctx.accounts.freeze_tokens(&[&signer_seeds])?; + Ok(()) +} diff --git a/programs/asset_controller/src/instructions/account/mod.rs b/programs/asset_controller/src/instructions/account/mod.rs index 9677e0f..c469854 100644 --- a/programs/asset_controller/src/instructions/account/mod.rs +++ b/programs/asset_controller/src/instructions/account/mod.rs @@ -1,5 +1,7 @@ pub mod close; -pub mod create; +pub mod freeze; +pub mod thaw; pub use close::*; -pub use create::*; +pub use freeze::*; +pub use thaw::*; diff --git a/programs/asset_controller/src/instructions/account/thaw.rs b/programs/asset_controller/src/instructions/account/thaw.rs new file mode 100644 index 0000000..bd2d167 --- /dev/null +++ b/programs/asset_controller/src/instructions/account/thaw.rs @@ -0,0 +1,52 @@ +use crate::state::*; +use anchor_lang::prelude::*; +use anchor_spl::{ + token_2022::{thaw_account, ThawAccount}, + token_interface::{Mint, Token2022, TokenAccount}, +}; +use rwa_utils::get_bump_in_seed_form; + +#[derive(Accounts)] +#[instruction()] +pub struct ThawTokenAccount<'info> { + #[account()] + pub authority: Signer<'info>, + #[account(mut)] + pub asset_mint: Box>, + #[account( + seeds = [asset_mint.key().as_ref()], + bump, + constraint = asset_controller.authority == authority.key() + )] + pub asset_controller: Box>, + #[account(mut)] + pub token_account: Box>, + pub token_program: Program<'info, Token2022>, +} + +impl<'info> ThawTokenAccount<'info> { + fn thaw_tokens(&self, signer_seeds: &[&[&[u8]]]) -> Result<()> { + let accounts = ThawAccount { + mint: self.asset_mint.to_account_info(), + authority: self.asset_controller.to_account_info(), + account: self.token_account.to_account_info(), + }; + let cpi_ctx = CpiContext::new_with_signer( + self.token_program.to_account_info(), + accounts, + signer_seeds, + ); + thaw_account(cpi_ctx)?; + Ok(()) + } +} + +pub fn handler(ctx: Context) -> Result<()> { + let asset_mint = ctx.accounts.asset_mint.key(); + let signer_seeds = [ + asset_mint.as_ref(), + &get_bump_in_seed_form(&ctx.bumps.asset_controller), + ]; + ctx.accounts.thaw_tokens(&[&signer_seeds])?; + Ok(()) +} diff --git a/programs/asset_controller/src/instructions/create.rs b/programs/asset_controller/src/instructions/create.rs index 62096a6..5123f81 100644 --- a/programs/asset_controller/src/instructions/create.rs +++ b/programs/asset_controller/src/instructions/create.rs @@ -9,13 +9,21 @@ use anchor_spl::{ TokenMetadataInitialize, }, }; -use spl_tlv_account_resolution::state::ExtraAccountMetaList; -use spl_transfer_hook_interface::instruction::ExecuteInstruction; - -use crate::{ - get_extra_account_metas, get_meta_list_size, state::*, - update_account_lamports_to_minimum_balance, +use data_registry::{ + cpi::{accounts::CreateDataRegistry, create_data_registry}, + program::DataRegistry, +}; +use identity_registry::{ + cpi::{accounts::CreateIdentityRegistry, create_identity_registry}, + program::IdentityRegistry, +}; +use policy_engine::{ + cpi::{accounts::CreatePolicyEngine, create_policy_engine}, + program::PolicyEngine, }; +use rwa_utils::get_bump_in_seed_form; + +use crate::{state::*, update_account_lamports_to_minimum_balance}; #[derive(AnchorSerialize, AnchorDeserialize)] pub struct CreateAssetControllerArgs { @@ -36,62 +44,142 @@ pub struct CreateAssetController<'info> { #[account()] /// CHECK: can be any account pub authority: UncheckedAccount<'info>, + #[account( + init, + payer = payer, + space = 8 + AssetControllerAccount::INIT_SPACE, + seeds = [asset_mint.key().as_ref()], + bump, + )] + pub asset_controller: Box>, #[account( init, signer, payer = payer, mint::token_program = token_program, mint::decimals = args.decimals, - mint::authority = authority, - mint::freeze_authority = authority, + mint::authority = asset_controller.key(), + mint::freeze_authority = asset_controller.key(), + extensions::permanent_delegate::delegate = asset_controller.key(), extensions::transfer_hook::authority = asset_controller.key(), - extensions::transfer_hook::program_id = crate::id(), + extensions::transfer_hook::program_id = policy_engine::id(), extensions::group_member_pointer::authority = asset_controller.key(), extensions::group_member_pointer::member_address = asset_mint.key(), extensions::group_pointer::authority = asset_controller.key(), extensions::group_pointer::group_address = asset_mint.key(), extensions::metadata_pointer::authority = asset_controller.key(), extensions::metadata_pointer::metadata_address = asset_mint.key(), - extensions::permanent_delegate::delegate = asset_controller.key(), extensions::interest_bearing_mint::authority = asset_controller.key(), extensions::interest_bearing_mint::rate = args.interest_rate.unwrap_or(0), extensions::close_authority::authority = asset_controller.key(), )] pub asset_mint: Box>, - #[account( - init, - payer = payer, - space = 8 + AssetControllerAccount::INIT_SPACE, - seeds = [asset_mint.key().as_ref()], - bump, - )] - pub asset_controller: Box>, - #[account( - init, - space = get_meta_list_size()?, - seeds = [META_LIST_ACCOUNT_SEED, asset_mint.key().as_ref()], - bump, - payer = payer, - )] - /// CHECK: extra metas account + #[account(mut)] + /// CHECK: cpi checks pub extra_metas_account: UncheckedAccount<'info>, + /// CHECK: cpi checks + #[account(mut)] + pub policy_engine_account: UncheckedAccount<'info>, + /// CHECK: cpi checks + #[account(mut)] + pub identity_registry_account: UncheckedAccount<'info>, + /// CHECK: cpi checks + #[account(mut)] + pub data_registry_account: UncheckedAccount<'info>, + pub policy_engine: Program<'info, PolicyEngine>, + pub identity_registry: Program<'info, IdentityRegistry>, + pub data_registry: Program<'info, DataRegistry>, pub system_program: Program<'info, System>, pub token_program: Program<'info, Token2022>, } impl<'info> CreateAssetController<'info> { - fn initialize_token_metadata(&self, name: String, symbol: String, uri: String) -> Result<()> { + fn initialize_token_metadata( + &self, + name: String, + symbol: String, + uri: String, + signer_seeds: &[&[&[u8]]], + ) -> Result<()> { let cpi_accounts = TokenMetadataInitialize { - token_program_id: self.token_program.to_account_info(), + program_id: self.token_program.to_account_info(), mint: self.asset_mint.to_account_info(), metadata: self.asset_mint.to_account_info(), // metadata account is the mint, since data is stored in mint - mint_authority: self.authority.to_account_info(), + mint_authority: self.asset_controller.to_account_info(), update_authority: self.asset_controller.to_account_info(), }; - let cpi_ctx = CpiContext::new(self.token_program.to_account_info(), cpi_accounts); + let cpi_ctx = CpiContext::new_with_signer( + self.token_program.to_account_info(), + cpi_accounts, + signer_seeds, + ); token_metadata_initialize(cpi_ctx, name, symbol, uri)?; Ok(()) } + + fn create_policy_engine( + &self, + delegate: Option, + signer_seeds: &[&[&[u8]]], + ) -> Result<()> { + let cpi_accounts = CreatePolicyEngine { + payer: self.payer.to_account_info(), + signer: self.asset_controller.to_account_info(), + asset_mint: self.asset_mint.to_account_info(), + extra_metas_account: self.extra_metas_account.to_account_info(), + policy_engine_account: self.policy_engine_account.to_account_info(), + system_program: self.system_program.to_account_info(), + }; + let cpi_ctx = CpiContext::new_with_signer( + self.policy_engine.to_account_info(), + cpi_accounts, + signer_seeds, + ); + create_policy_engine(cpi_ctx, self.authority.key(), delegate)?; + Ok(()) + } + + fn create_identity_registry( + &self, + delegate: Option, + signer_seeds: &[&[&[u8]]], + ) -> Result<()> { + let cpi_accounts = CreateIdentityRegistry { + payer: self.payer.to_account_info(), + signer: self.asset_controller.to_account_info(), + asset_mint: self.asset_mint.to_account_info(), + identity_registry_account: self.identity_registry_account.to_account_info(), + system_program: self.system_program.to_account_info(), + }; + let cpi_ctx = CpiContext::new_with_signer( + self.identity_registry.to_account_info(), + cpi_accounts, + signer_seeds, + ); + create_identity_registry(cpi_ctx, self.authority.key(), delegate)?; + Ok(()) + } + + fn create_data_registry( + &self, + delegate: Option, + signer_seeds: &[&[&[u8]]], + ) -> Result<()> { + let cpi_accounts = CreateDataRegistry { + payer: self.payer.to_account_info(), + signer: self.asset_controller.to_account_info(), + asset_mint: self.asset_mint.to_account_info(), + data_registry: self.data_registry_account.to_account_info(), + system_program: self.system_program.to_account_info(), + }; + let cpi_ctx = CpiContext::new_with_signer( + self.data_registry.to_account_info(), + cpi_accounts, + signer_seeds, + ); + create_data_registry(cpi_ctx, self.authority.key(), delegate)?; + Ok(()) + } } pub fn handler(ctx: Context, args: CreateAssetControllerArgs) -> Result<()> { @@ -100,18 +188,19 @@ pub fn handler(ctx: Context, args: CreateAssetControllerA ctx.accounts.authority.key(), args.delegate, ); + let asset_mint = ctx.accounts.asset_mint.key(); - // initialize the extra metas account - let extra_metas_account = &ctx.accounts.extra_metas_account; - let metas = get_extra_account_metas()?; - let mut data = extra_metas_account.try_borrow_mut_data()?; - ExtraAccountMetaList::init::(&mut data, &metas)?; + let signer_seeds = [ + asset_mint.as_ref(), + &get_bump_in_seed_form(&ctx.bumps.asset_controller), + ]; // initialize token metadata ctx.accounts.initialize_token_metadata( args.name.clone(), args.symbol.clone(), args.uri.clone(), + &[&signer_seeds], )?; // transfer minimum rent to mint account @@ -140,5 +229,17 @@ pub fn handler(ctx: Context, args: CreateAssetControllerA .map_err(|_| ProgramError::InvalidAccountData)?, }); + // create policy registry + ctx.accounts + .create_policy_engine(args.delegate, &[&signer_seeds])?; + + // create identity registry + ctx.accounts + .create_identity_registry(args.delegate, &[&signer_seeds])?; + + // create data registry + ctx.accounts + .create_data_registry(args.delegate, &[&signer_seeds])?; + Ok(()) } diff --git a/programs/asset_controller/src/instructions/extensions/close_mint.rs b/programs/asset_controller/src/instructions/extensions/close_mint.rs index 5c0d305..f6a8edf 100644 --- a/programs/asset_controller/src/instructions/extensions/close_mint.rs +++ b/programs/asset_controller/src/instructions/extensions/close_mint.rs @@ -1,4 +1,4 @@ -use anchor_lang::{prelude::*, solana_program::program_option::COption}; +use anchor_lang::prelude::*; use anchor_spl::{ token_2022::{close_account, CloseAccount}, token_interface::{Mint, Token2022}, @@ -14,7 +14,6 @@ pub struct CloseMintAccount<'info> { pub authority: Signer<'info>, #[account( mut, - constraint = asset_mint.mint_authority == COption::Some(authority.key()), constraint = asset_mint.supply == 0, )] pub asset_mint: Box>, diff --git a/programs/asset_controller/src/instructions/extensions/memo.rs b/programs/asset_controller/src/instructions/extensions/disable_memo.rs similarity index 100% rename from programs/asset_controller/src/instructions/extensions/memo.rs rename to programs/asset_controller/src/instructions/extensions/disable_memo.rs diff --git a/programs/asset_controller/src/instructions/account/create.rs b/programs/asset_controller/src/instructions/extensions/enable_memo.rs similarity index 57% rename from programs/asset_controller/src/instructions/account/create.rs rename to programs/asset_controller/src/instructions/extensions/enable_memo.rs index 2d13984..65bfaad 100644 --- a/programs/asset_controller/src/instructions/account/create.rs +++ b/programs/asset_controller/src/instructions/extensions/enable_memo.rs @@ -5,54 +5,35 @@ use anchor_spl::{ token_interface::{memo_transfer_initialize, MemoTransfer, Mint, Token2022, TokenAccount}, }; -use crate::{AssetControllerAccount, ExtensionMetadataEvent, TrackerAccount}; +use crate::ExtensionMetadataEvent; #[derive(AnchorDeserialize, AnchorSerialize)] -pub struct CreateTokenAccountArgs { +pub struct EnableMemoTransferArgs { pub memo_transfer: bool, } #[derive(Accounts)] #[instruction()] #[event_cpi] -pub struct CreateTokenAccount<'info> { +pub struct EnableMemoTransfer<'info> { #[account(mut)] pub payer: Signer<'info>, - #[account()] - /// CHECK: can be any account - pub owner: UncheckedAccount<'info>, - // Note: All ATAs are are initialized with ImmutableOwner because the mint is created with Token22. - #[account( - mint::token_program = token_program, - )] + pub owner: Signer<'info>, pub asset_mint: Box>, #[account( init_if_needed, payer = payer, - associated_token::token_program = token_program, associated_token::mint = asset_mint, associated_token::authority = owner, + associated_token::token_program = token_program, )] pub token_account: Box>, - #[account( - init_if_needed, - space = 8 + TrackerAccount::INIT_SPACE, - seeds = [asset_mint.key().as_ref(), owner.key().as_ref()], - bump, - payer = payer, - )] - pub tracker_account: Box>, - #[account( - seeds = [asset_mint.key().as_ref()], - bump, - )] - pub asset_controller: Account<'info, AssetControllerAccount>, - pub system_program: Program<'info, System>, pub token_program: Program<'info, Token2022>, pub associated_token_program: Program<'info, AssociatedToken>, + pub system_program: Program<'info, System>, } -impl<'info> CreateTokenAccount<'info> { +impl<'info> EnableMemoTransfer<'info> { #[allow(clippy::needless_borrow)] fn reallocate_ta(&self, extensions: Vec) -> Result<()> { let ix = reallocate( @@ -69,7 +50,7 @@ impl<'info> CreateTokenAccount<'info> { self.token_account.to_account_info(), self.payer.to_account_info(), self.system_program.to_account_info(), - self.owner.to_account_info(), + self.payer.to_account_info(), ], )?; Ok(()) @@ -87,22 +68,15 @@ impl<'info> CreateTokenAccount<'info> { } } -pub const TOKEN_EXTENSIONS: [ExtensionType; 1] = [ExtensionType::MemoTransfer]; - -pub fn handler(ctx: Context, args: CreateTokenAccountArgs) -> Result<()> { +pub fn handler(ctx: Context) -> Result<()> { ctx.accounts - .tracker_account - .new(ctx.accounts.asset_mint.key(), ctx.accounts.owner.key()); - - if args.memo_transfer { - ctx.accounts.reallocate_ta(TOKEN_EXTENSIONS.to_vec())?; - ctx.accounts.enable_memo_transfer()?; - emit_cpi!(ExtensionMetadataEvent { - address: ctx.accounts.token_account.key().to_string(), - extension_type: ExtensionType::MemoTransfer as u8, - metadata: vec![1] - }); - } + .reallocate_ta(vec![ExtensionType::MemoTransfer])?; + ctx.accounts.enable_memo_transfer()?; + emit_cpi!(ExtensionMetadataEvent { + address: ctx.accounts.token_account.key().to_string(), + extension_type: ExtensionType::MemoTransfer as u8, + metadata: vec![1] + }); Ok(()) } diff --git a/programs/asset_controller/src/instructions/extensions/interest_bearing.rs b/programs/asset_controller/src/instructions/extensions/interest_bearing.rs index 310e41b..1bc1fe8 100644 --- a/programs/asset_controller/src/instructions/extensions/interest_bearing.rs +++ b/programs/asset_controller/src/instructions/extensions/interest_bearing.rs @@ -1,4 +1,4 @@ -use anchor_lang::{prelude::*, solana_program::program_option::COption}; +use anchor_lang::prelude::*; use anchor_spl::token_interface::{ get_mint_extension_data, interest_bearing_mint_update_rate, InterestBearingMintUpdateRate, Mint, Token2022, @@ -14,10 +14,7 @@ use crate::{AssetControllerAccount, ExtensionMetadataEvent}; pub struct UpdateInterestBearingMintRate<'info> { #[account()] pub authority: Signer<'info>, - #[account( - mut, - constraint = asset_mint.mint_authority == COption::Some(authority.key()), - )] + #[account(mut)] pub asset_mint: Box>, #[account( seeds = [asset_mint.key().as_ref()], diff --git a/programs/asset_controller/src/instructions/extensions/mod.rs b/programs/asset_controller/src/instructions/extensions/mod.rs index ae1096f..a596a6e 100644 --- a/programs/asset_controller/src/instructions/extensions/mod.rs +++ b/programs/asset_controller/src/instructions/extensions/mod.rs @@ -1,7 +1,9 @@ pub mod close_mint; +pub mod disable_memo; +pub mod enable_memo; pub mod interest_bearing; -pub mod memo; pub use close_mint::*; +pub use disable_memo::*; +pub use enable_memo::*; pub use interest_bearing::*; -pub use memo::*; diff --git a/programs/asset_controller/src/instructions/mod.rs b/programs/asset_controller/src/instructions/mod.rs index 0ee9c39..3a2a8e4 100644 --- a/programs/asset_controller/src/instructions/mod.rs +++ b/programs/asset_controller/src/instructions/mod.rs @@ -1,7 +1,6 @@ pub mod account; pub mod create; pub mod delegate; -pub mod execute; pub mod extensions; pub mod token; pub mod update; @@ -9,7 +8,6 @@ pub mod update; pub use account::*; pub use create::*; pub use delegate::*; -pub use execute::*; pub use extensions::*; pub use token::*; pub use update::*; diff --git a/programs/asset_controller/src/instructions/token/void.rs b/programs/asset_controller/src/instructions/token/burn.rs similarity index 100% rename from programs/asset_controller/src/instructions/token/void.rs rename to programs/asset_controller/src/instructions/token/burn.rs diff --git a/programs/asset_controller/src/instructions/token/issue.rs b/programs/asset_controller/src/instructions/token/issue.rs index 5758e89..4e7deb8 100644 --- a/programs/asset_controller/src/instructions/token/issue.rs +++ b/programs/asset_controller/src/instructions/token/issue.rs @@ -1,39 +1,63 @@ -use anchor_lang::{prelude::*, solana_program::program_option::COption}; -use anchor_spl::token_interface::{mint_to, Mint, MintTo, Token2022, TokenAccount}; +use anchor_lang::prelude::*; +use anchor_spl::{ + associated_token::AssociatedToken, + token_interface::{mint_to, Mint, MintTo, Token2022, TokenAccount}, +}; +use rwa_utils::get_bump_in_seed_form; -#[derive(AnchorDeserialize, AnchorSerialize)] -pub struct IssueTokensArgs { - pub amount: u64, - pub to: Pubkey, -} +use crate::AssetControllerAccount; #[derive(Accounts)] -#[instruction(args: IssueTokensArgs)] +#[instruction()] pub struct IssueTokens<'info> { - #[account()] + #[account(mut)] pub authority: Signer<'info>, + #[account(mut)] + pub asset_mint: Box>, #[account( - mut, - constraint = asset_mint.mint_authority == COption::Some(authority.key()), + seeds = [asset_mint.key().as_ref()], + bump, + constraint = asset_controller.authority == authority.key(), )] - pub asset_mint: Box>, + pub asset_controller: Box>, + /// CHECK: can be any account + pub to: UncheckedAccount<'info>, #[account( - mut, + init_if_needed, + payer = authority, associated_token::token_program = token_program, associated_token::mint = asset_mint, - associated_token::authority = args.to, + associated_token::authority = to, )] pub token_account: Box>, pub token_program: Program<'info, Token2022>, + pub associated_token_program: Program<'info, AssociatedToken>, + pub system_program: Program<'info, System>, +} + +impl<'info> IssueTokens<'info> { + fn issue_tokens(&self, amount: u64, signer_seeds: &[&[&[u8]]]) -> Result<()> { + let accounts = MintTo { + mint: self.asset_mint.to_account_info(), + to: self.token_account.to_account_info(), + authority: self.asset_controller.to_account_info(), + }; + let cpi_ctx = CpiContext::new_with_signer( + self.token_program.to_account_info(), + accounts, + signer_seeds, + ); + mint_to(cpi_ctx, amount)?; + Ok(()) + } } -pub fn handler(ctx: Context, args: IssueTokensArgs) -> Result<()> { - let accounts = MintTo { - mint: ctx.accounts.asset_mint.to_account_info(), - to: ctx.accounts.token_account.to_account_info(), - authority: ctx.accounts.authority.to_account_info(), - }; - let cpi_ctx = CpiContext::new(ctx.accounts.token_program.to_account_info(), accounts); - mint_to(cpi_ctx, args.amount)?; +pub fn handler(ctx: Context, amount: u64) -> Result<()> { + let asset_mint = ctx.accounts.asset_mint.key(); + let signer_seeds = [ + asset_mint.as_ref(), + &get_bump_in_seed_form(&ctx.bumps.asset_controller), + ]; + ctx.accounts.issue_tokens(amount, &[&signer_seeds])?; Ok(()) } diff --git a/programs/asset_controller/src/instructions/token/mod.rs b/programs/asset_controller/src/instructions/token/mod.rs index f3513c7..fa0656f 100644 --- a/programs/asset_controller/src/instructions/token/mod.rs +++ b/programs/asset_controller/src/instructions/token/mod.rs @@ -1,5 +1,7 @@ +pub mod burn; pub mod issue; -pub mod void; +pub mod revoke; +pub use burn::*; pub use issue::*; -pub use void::*; +pub use revoke::*; diff --git a/programs/asset_controller/src/instructions/token/revoke.rs b/programs/asset_controller/src/instructions/token/revoke.rs new file mode 100644 index 0000000..ff96b34 --- /dev/null +++ b/programs/asset_controller/src/instructions/token/revoke.rs @@ -0,0 +1,110 @@ +use crate::state::*; +use anchor_lang::prelude::*; +use anchor_spl::{ + associated_token::AssociatedToken, + token_2022::{burn, Burn}, + token_interface::{Mint, Token2022, TokenAccount}, +}; +use rwa_utils::get_bump_in_seed_form; +use spl_token_2022::instruction::transfer_checked; +use spl_transfer_hook_interface::onchain::add_extra_accounts_for_execute_cpi; + +#[derive(Accounts)] +#[instruction()] +pub struct RevokeTokens<'info> { + #[account(mut)] + pub authority: Signer<'info>, + #[account(mut)] + pub asset_mint: Box>, + #[account( + seeds = [asset_mint.key().as_ref()], + bump, + constraint = asset_controller.authority == authority.key() + )] + pub asset_controller: Box>, + #[account( + init_if_needed, + payer = authority, + associated_token::mint = asset_mint, + associated_token::authority = asset_controller, + associated_token::token_program = token_program, + )] + pub authority_token_account: Box>, + #[account(mut)] + pub revoke_token_account: Box>, + pub token_program: Program<'info, Token2022>, + pub associated_token_program: Program<'info, AssociatedToken>, + pub system_program: Program<'info, System>, +} + +impl<'info> RevokeTokens<'info> { + fn transfer_tokens( + &self, + amount: u64, + signer_seeds: &[&[&[u8]]], + remaining_accounts: &[AccountInfo<'info>], + ) -> Result<()> { + let mut ix = transfer_checked( + self.token_program.key, + &self.revoke_token_account.key(), + &self.asset_mint.key(), + &self.authority_token_account.key(), + &self.asset_controller.key(), + &[], + amount, + self.asset_mint.decimals, + )?; + + let mut account_infos = vec![ + self.revoke_token_account.to_account_info(), + self.asset_mint.to_account_info(), + self.authority_token_account.to_account_info(), + self.asset_controller.to_account_info(), + ]; + + add_extra_accounts_for_execute_cpi( + &mut ix, + &mut account_infos, + &policy_engine::id(), + self.revoke_token_account.to_account_info(), + self.asset_mint.to_account_info(), + self.authority_token_account.to_account_info(), + self.asset_controller.to_account_info(), + amount, + remaining_accounts, + )?; + + anchor_lang::solana_program::program::invoke_signed(&ix, &account_infos, signer_seeds) + .map_err(Into::into) + } + + fn burn_tokens(&self, amount: u64, signer_seeds: &[&[&[u8]]]) -> Result<()> { + let accounts = Burn { + mint: self.asset_mint.to_account_info(), + authority: self.asset_controller.to_account_info(), + from: self.authority_token_account.to_account_info(), + }; + let cpi_ctx = CpiContext::new_with_signer( + self.token_program.to_account_info(), + accounts, + signer_seeds, + ); + burn(cpi_ctx, amount)?; + Ok(()) + } +} + +pub fn handler<'info>( + ctx: Context<'_, '_, '_, 'info, RevokeTokens<'info>>, + amount: u64, +) -> Result<()> { + let asset_mint = ctx.accounts.asset_mint.key(); + let signer_seeds = [ + asset_mint.as_ref(), + &get_bump_in_seed_form(&ctx.bumps.asset_controller), + ]; + ctx.accounts + .transfer_tokens(amount, &[&signer_seeds], ctx.remaining_accounts)?; + ctx.accounts.burn_tokens(amount, &[&signer_seeds])?; + Ok(()) +} diff --git a/programs/asset_controller/src/instructions/update.rs b/programs/asset_controller/src/instructions/update.rs index 3984cc2..bf43852 100644 --- a/programs/asset_controller/src/instructions/update.rs +++ b/programs/asset_controller/src/instructions/update.rs @@ -23,16 +23,12 @@ pub struct UpdateAssetMetadata<'info> { pub payer: Signer<'info>, #[account(mut)] pub authority: Signer<'info>, - #[account( - mut, - mint::token_program = token_program, - mint::authority = authority, - )] + #[account(mut)] pub asset_mint: Box>, #[account( seeds = [asset_mint.key().as_ref()], bump, - constraint = asset_controller.authority == *authority.key, + constraint = asset_controller.authority == authority.key(), )] pub asset_controller: Box>, pub system_program: Program<'info, System>, @@ -47,7 +43,7 @@ impl<'info> UpdateAssetMetadata<'info> { signer_seeds: &[&[&[u8]]], ) -> Result<()> { let cpi_accounts = TokenMetadataUpdateField { - token_program_id: self.token_program.to_account_info(), + program_id: self.token_program.to_account_info(), metadata: self.asset_mint.to_account_info(), // metadata account is the mint, since data is stored in mint update_authority: self.asset_controller.to_account_info(), }; diff --git a/programs/asset_controller/src/lib.rs b/programs/asset_controller/src/lib.rs index 36fe39b..d771eb3 100644 --- a/programs/asset_controller/src/lib.rs +++ b/programs/asset_controller/src/lib.rs @@ -27,8 +27,8 @@ pub mod asset_controller { } /// issue shares of the rwa asset - pub fn issue_tokens(ctx: Context, args: IssueTokensArgs) -> Result<()> { - instructions::issue::handler(ctx, args) + pub fn issue_tokens(ctx: Context, amount: u64) -> Result<()> { + instructions::issue::handler(ctx, amount) } /// edit metadata of the rwa asset @@ -39,17 +39,17 @@ pub mod asset_controller { instructions::update::handler(ctx, args) } - /// void shares of the rwa asset - pub fn void_tokens(ctx: Context, amount: u64) -> Result<()> { - instructions::void::handler(ctx, amount) + /// burn shares of the rwa asset + pub fn burn_tokens(ctx: Context, amount: u64) -> Result<()> { + instructions::burn::handler(ctx, amount) } - /// create a token account - pub fn create_token_account( - ctx: Context, - args: CreateTokenAccountArgs, + /// revoke shares of the rwa asset + pub fn revoke_tokens<'info>( + ctx: Context<'_, '_, '_, 'info, RevokeTokens<'info>>, + amount: u64, ) -> Result<()> { - instructions::account::create::handler(ctx, args) + instructions::revoke::handler(ctx, amount) } /// close a token account @@ -57,9 +57,14 @@ pub mod asset_controller { instructions::account::close::handler(ctx) } + /// memo transfer enable + pub fn enable_memo_transfer(ctx: Context) -> Result<()> { + instructions::extensions::enable_memo::handler(ctx) + } + /// memo transfer disable pub fn disable_memo_transfer(ctx: Context) -> Result<()> { - instructions::extensions::memo::handler(ctx) + instructions::extensions::disable_memo::handler(ctx) } /// interest bearing mint rate update @@ -75,9 +80,13 @@ pub mod asset_controller { instructions::extensions::close_mint::handler(ctx) } - /// execute transfer hook - #[interface(spl_transfer_hook_interface::execute)] - pub fn execute_transaction(ctx: Context, amount: u64) -> Result<()> { - instructions::execute::handler(ctx, amount) + /// freeze token account + pub fn freeze_token_account(ctx: Context) -> Result<()> { + instructions::account::freeze::handler(ctx) + } + + /// thaw token account + pub fn thaw_token_account(ctx: Context) -> Result<()> { + instructions::account::thaw::handler(ctx) } } diff --git a/programs/asset_controller/src/state/mod.rs b/programs/asset_controller/src/state/mod.rs index c3a38e5..4fbf4d2 100644 --- a/programs/asset_controller/src/state/mod.rs +++ b/programs/asset_controller/src/state/mod.rs @@ -1,17 +1,12 @@ -pub const META_LIST_ACCOUNT_SEED: &[u8] = b"extra-account-metas"; - pub mod registry; -pub mod track; pub use registry::*; -pub use track::*; use anchor_lang::{solana_program::program_error::ProgramError, AnchorDeserialize, Discriminator}; use rwa_utils::GeyserProgramAccount; pub enum AssetControllerAccounts { AssetControllerAccount(AssetControllerAccount), - TrackerAccount(TrackerAccount), } impl GeyserProgramAccount for AssetControllerAccounts { @@ -20,7 +15,6 @@ impl GeyserProgramAccount for AssetControllerAccounts { AssetControllerAccounts::AssetControllerAccount(_) => { AssetControllerAccount::DISCRIMINATOR } - AssetControllerAccounts::TrackerAccount(_) => TrackerAccount::DISCRIMINATOR, } } @@ -42,10 +36,6 @@ impl GeyserProgramAccount for AssetControllerAccounts { let account = AssetControllerAccount::deserialize(account_data)?; Ok(AssetControllerAccounts::AssetControllerAccount(account)) } - TrackerAccount::DISCRIMINATOR => { - let account = TrackerAccount::deserialize(account_data)?; - Ok(AssetControllerAccounts::TrackerAccount(account)) - } _ => Err(ProgramError::InvalidAccountData), } } diff --git a/programs/asset_controller/src/state/registry.rs b/programs/asset_controller/src/state/registry.rs index 7036efc..e957c69 100644 --- a/programs/asset_controller/src/state/registry.rs +++ b/programs/asset_controller/src/state/registry.rs @@ -22,6 +22,9 @@ impl AssetControllerAccount { self.delegate = delegate.unwrap_or(authority); self.version = Self::VERSION; } + pub fn get_pda(asset_mint: Pubkey) -> Pubkey { + Pubkey::find_program_address(&[asset_mint.as_ref()], &crate::id()).0 + } } #[event] diff --git a/programs/asset_controller/src/utils.rs b/programs/asset_controller/src/utils.rs index 79529b3..c4c3264 100644 --- a/programs/asset_controller/src/utils.rs +++ b/programs/asset_controller/src/utils.rs @@ -1,86 +1,10 @@ use anchor_lang::{ prelude::Result, - solana_program::{ - program::invoke, - pubkey::Pubkey, - system_instruction::transfer, - sysvar::{self, instructions::get_instruction_relative}, - }, + solana_program::{program::invoke, system_instruction::transfer}, Lamports, }; -use anchor_spl::token_2022; -use spl_tlv_account_resolution::{ - account::ExtraAccountMeta, seeds::Seed, state::ExtraAccountMetaList, -}; - -use crate::{id, AccountInfo, AssetControllerErrors, Rent, SolanaSysvar}; - -pub fn get_meta_list_size() -> Result { - Ok(ExtraAccountMetaList::size_of(get_extra_account_metas()?.len()).unwrap()) -} - -pub fn get_extra_account_metas() -> Result> { - Ok(vec![ - // policy engine program - ExtraAccountMeta::new_with_pubkey(&policy_engine::id(), false, false)?, - // policy engine account - ExtraAccountMeta::new_external_pda_with_seeds( - 5, - &[Seed::AccountKey { index: 1 }], - false, - false, - )?, - // identity program - ExtraAccountMeta::new_with_pubkey(&identity_registry::id(), false, false)?, - // identity registry account - ExtraAccountMeta::new_external_pda_with_seeds( - 7, - &[Seed::AccountKey { index: 1 }], - false, - false, - )?, - // user identity account - ExtraAccountMeta::new_external_pda_with_seeds( - 7, - &[ - Seed::AccountKey { index: 8 }, - Seed::AccountData { - account_index: 2, - data_index: 32, - length: 32, - }, - ], - false, - true, - )?, - // user tracker account - ExtraAccountMeta::new_with_seeds( - &[ - Seed::AccountKey { index: 1 }, - Seed::AccountData { - account_index: 2, - data_index: 32, - length: 32, - }, - ], - false, - true, - )?, - // policy account - ExtraAccountMeta::new_external_pda_with_seeds( - 5, - &[Seed::AccountKey { index: 6 }], - false, - false, - )?, - // instructions program - ExtraAccountMeta::new_with_pubkey(&sysvar::instructions::id(), false, false)?, - ]) -} -pub fn get_transaction_approval_account_pda(mint: Pubkey) -> Pubkey { - Pubkey::find_program_address(&[mint.as_ref()], &id()).0 -} +use crate::{AccountInfo, Rent, SolanaSysvar}; pub fn update_account_lamports_to_minimum_balance<'info>( account: AccountInfo<'info>, @@ -96,38 +20,3 @@ pub fn update_account_lamports_to_minimum_balance<'info>( } Ok(()) } - -#[inline(never)] -pub fn verify_pda(address: Pubkey, seeds: &[&[u8]], program_id: &Pubkey) -> Result<()> { - let (pda, _) = Pubkey::find_program_address(seeds, program_id); - if pda != address { - return Err(AssetControllerErrors::InvalidPdaPassedIn.into()); - } - Ok(()) -} - -pub const TRANSFER_HOOK_MINT_INDEX: usize = 1; - -pub fn verify_cpi_program_is_token22( - instructions_program: &AccountInfo, - amount: u64, - mint: Pubkey, -) -> Result<()> { - let ix_relative = get_instruction_relative(0, instructions_program)?; - if ix_relative.program_id != token_2022::ID { - return Err(AssetControllerErrors::InvalidCpiTransferProgram.into()); - } - if ix_relative.data[1..9] != amount.to_le_bytes() { - return Err(AssetControllerErrors::InvalidCpiTransferAmount.into()); - } - // make sure transfer mint is same - if let Some(account) = ix_relative.accounts.get(TRANSFER_HOOK_MINT_INDEX) { - if account.pubkey != mint { - return Err(AssetControllerErrors::InvalidCpiTransferMint.into()); - } - } else { - return Err(AssetControllerErrors::InvalidCpiTransferProgram.into()); - } - - Ok(()) -} diff --git a/programs/data_registry/src/instructions/account/delete.rs b/programs/data_registry/src/instructions/account/delete.rs index da7e1f2..e61a1bf 100644 --- a/programs/data_registry/src/instructions/account/delete.rs +++ b/programs/data_registry/src/instructions/account/delete.rs @@ -5,6 +5,7 @@ use anchor_lang::prelude::*; #[instruction()] pub struct DeleteDataAccount<'info> { #[account( + mut, constraint = data_registry.authority == signer.key() || data_registry.delegate == signer.key() )] pub signer: Signer<'info>, diff --git a/programs/data_registry/src/instructions/registry/create.rs b/programs/data_registry/src/instructions/registry/create.rs index 30855bf..ebf4efe 100644 --- a/programs/data_registry/src/instructions/registry/create.rs +++ b/programs/data_registry/src/instructions/registry/create.rs @@ -1,7 +1,6 @@ use crate::state::*; use anchor_lang::{prelude::*, solana_program::program_option::COption}; use anchor_spl::token_interface::Mint; -use rwa_utils::TOKEN22; #[derive(Accounts)] #[instruction()] @@ -12,9 +11,7 @@ pub struct CreateDataRegistry<'info> { constraint = asset_mint.mint_authority == COption::Some(signer.key()), )] pub signer: Signer<'info>, - #[account( - mint::token_program = TOKEN22, - )] + #[account()] pub asset_mint: Box>, #[account( init, diff --git a/programs/identity_registry/src/instructions/account/add.rs b/programs/identity_registry/src/instructions/account/add.rs index 7520e64..68a63e3 100644 --- a/programs/identity_registry/src/instructions/account/add.rs +++ b/programs/identity_registry/src/instructions/account/add.rs @@ -19,6 +19,7 @@ pub struct AddLevelToIdentityAccount<'info> { realloc = identity_account.to_account_info().data_len() + 1, // u8 realloc::zero = false, realloc::payer = payer, + constraint = level != 0, )] pub identity_account: Box>, pub system_program: Program<'info, System>, diff --git a/programs/identity_registry/src/instructions/account/create.rs b/programs/identity_registry/src/instructions/account/create.rs index af67f0d..50f9518 100644 --- a/programs/identity_registry/src/instructions/account/create.rs +++ b/programs/identity_registry/src/instructions/account/create.rs @@ -2,7 +2,7 @@ use crate::state::*; use anchor_lang::prelude::*; #[derive(Accounts)] -#[instruction(owner: Pubkey)] +#[instruction(owner: Pubkey, level: u8)] pub struct CreateIdentityAccount<'info> { #[account(mut)] pub payer: Signer<'info>, @@ -18,6 +18,7 @@ pub struct CreateIdentityAccount<'info> { seeds = [identity_registry.key().as_ref(), owner.as_ref()], bump, payer = payer, + constraint = level != 0, )] pub identity_account: Box>, pub system_program: Program<'info, System>, diff --git a/programs/identity_registry/src/state/account.rs b/programs/identity_registry/src/state/account.rs index bf962b6..abbcb31 100644 --- a/programs/identity_registry/src/state/account.rs +++ b/programs/identity_registry/src/state/account.rs @@ -16,7 +16,7 @@ pub struct IdentityRegistryAccount { pub const SKIP_POLICY_LEVEL: u8 = u8::MAX; /// level to be used if user does not have any identity -pub const NO_IDENTITY_LEVEL: u8 = u8::MIN; +pub const NO_IDENTITY_LEVEL: u8 = u8::MAX - 1; impl IdentityRegistryAccount { pub const VERSION: u8 = 1; diff --git a/programs/policy_engine/Cargo.toml b/programs/policy_engine/Cargo.toml index 427beaa..df8833d 100644 --- a/programs/policy_engine/Cargo.toml +++ b/programs/policy_engine/Cargo.toml @@ -17,9 +17,13 @@ default = [] idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"] [dependencies] -anchor-lang = { git = "https://github.com/bridgesplit/anchor" } -anchor-spl = { git = "https://github.com/bridgesplit/anchor" } +anchor-lang = { git = "https://github.com/bridgesplit/anchor", features = ["interface-instructions", "event-cpi"] } +anchor-spl = { git = "https://github.com/bridgesplit/anchor", features = ["token_2022_extensions"] } rwa_utils = { path = "../rwa_utils" } +identity_registry = { path = "../identity_registry", features = ["no-entrypoint"] } + serde = "1.0" num_enum = "0.7.2" sha256 = "1.5.0" +spl-tlv-account-resolution = "0.6.3" +spl-transfer-hook-interface = { version = "0.6.3" } diff --git a/programs/policy_engine/src/error.rs b/programs/policy_engine/src/error.rs index e34232f..a433c5a 100644 --- a/programs/policy_engine/src/error.rs +++ b/programs/policy_engine/src/error.rs @@ -24,4 +24,14 @@ pub enum PolicyEngineErrors { PolicyAlreadyExists, #[msg("Max balance exceeded")] MaxBalanceExceeded, + #[msg("Invalid CPI transfer amount")] + InvalidCpiTransferAmount, + #[msg("Invalid CPI transfer mint")] + InvalidCpiTransferMint, + #[msg("Invalid CPI transfer program")] + InvalidCpiTransferProgram, + #[msg("Invalid PDA passed in")] + InvalidPdaPassedIn, + #[msg("Transfer history full")] + TransferHistoryFull, } diff --git a/programs/policy_engine/src/instructions/engine/create.rs b/programs/policy_engine/src/instructions/engine/create.rs index 52e3b10..1582d48 100644 --- a/programs/policy_engine/src/instructions/engine/create.rs +++ b/programs/policy_engine/src/instructions/engine/create.rs @@ -1,8 +1,10 @@ use anchor_lang::{prelude::*, solana_program::program_option::COption}; use anchor_spl::token_interface::Mint; -use rwa_utils::TOKEN22; +use rwa_utils::META_LIST_ACCOUNT_SEED; +use spl_tlv_account_resolution::state::ExtraAccountMetaList; +use spl_transfer_hook_interface::instruction::ExecuteInstruction; -use crate::state::*; +use crate::{get_extra_account_metas, get_meta_list_size, state::*}; #[derive(Accounts)] #[instruction()] @@ -13,9 +15,7 @@ pub struct CreatePolicyEngine<'info> { constraint = asset_mint.mint_authority == COption::Some(signer.key()), )] pub signer: Signer<'info>, - #[account( - mint::token_program = TOKEN22 - )] + #[account()] pub asset_mint: Box>, #[account( init, @@ -24,7 +24,16 @@ pub struct CreatePolicyEngine<'info> { bump, payer = payer, )] - pub policy_engine: Box>, + pub policy_engine_account: Box>, + #[account( + init, + space = get_meta_list_size()?, + seeds = [META_LIST_ACCOUNT_SEED, asset_mint.key().as_ref()], + bump, + payer = payer, + )] + /// CHECK: extra metas account + pub extra_metas_account: UncheckedAccount<'info>, pub system_program: Program<'info, System>, } @@ -33,8 +42,14 @@ pub fn handler( authority: Pubkey, delegate: Option, ) -> Result<()> { + // initialize the extra metas account + let extra_metas_account = &ctx.accounts.extra_metas_account; + let metas = get_extra_account_metas()?; + let mut data = extra_metas_account.try_borrow_mut_data()?; + ExtraAccountMetaList::init::(&mut data, &metas)?; + ctx.accounts - .policy_engine + .policy_engine_account .new(authority, delegate, ctx.accounts.asset_mint.key()); Ok(()) diff --git a/programs/asset_controller/src/instructions/execute.rs b/programs/policy_engine/src/instructions/execute.rs similarity index 68% rename from programs/asset_controller/src/instructions/execute.rs rename to programs/policy_engine/src/instructions/execute.rs index a975f22..c911bcb 100644 --- a/programs/asset_controller/src/instructions/execute.rs +++ b/programs/policy_engine/src/instructions/execute.rs @@ -1,3 +1,7 @@ +use crate::{ + enforce_policy, get_asset_controller_account_pda, verify_cpi_program_is_token22, verify_pda, + PolicyAccount, PolicyEngineAccount, TrackerAccount, +}; use anchor_lang::{ prelude::*, solana_program::sysvar::{self}, @@ -6,18 +10,12 @@ use anchor_spl::token_interface::{Mint, TokenAccount}; use identity_registry::{ program::IdentityRegistry, IdentityAccount, NO_IDENTITY_LEVEL, SKIP_POLICY_LEVEL, }; -use policy_engine::{enforce_policy, program::PolicyEngine, PolicyAccount, PolicyEngineAccount}; - -use crate::{state::*, verify_cpi_program_is_token22, verify_pda}; +use rwa_utils::META_LIST_ACCOUNT_SEED; #[derive(Accounts)] #[instruction(amount: u64)] pub struct ExecuteTransferHook<'info> { - #[account( - associated_token::token_program = anchor_spl::token_interface::spl_token_2022::id(), - associated_token::authority = owner_delegate, - associated_token::mint = asset_mint, - )] + #[account()] pub source_account: Box>, #[account( token::token_program = anchor_spl::token_interface::spl_token_2022::id(), @@ -37,26 +35,17 @@ pub struct ExecuteTransferHook<'info> { bump, )] pub extra_metas_account: UncheckedAccount<'info>, - pub policy_engine: Program<'info, PolicyEngine>, - #[account( - owner = policy_engine.key(), - )] /// CHECK: internal ix checks pub policy_engine_account: UncheckedAccount<'info>, pub identity_registry: Program<'info, IdentityRegistry>, - #[account()] /// CHECK: internal ix checks pub identity_registry_account: UncheckedAccount<'info>, - #[account(mut)] /// CHECK: internal ix checks pub identity_account: UncheckedAccount<'info>, - #[account( - mut, - constraint = tracker_account.owner == destination_account.owner, - constraint = tracker_account.asset_mint == asset_mint.key(), - )] - pub tracker_account: Box>, - #[account()] + #[account(mut)] + /// CHECK: internal ix checks + pub tracker_account: UncheckedAccount<'info>, + #[account(mut)] /// CHECK: internal ix checks pub policy_account: UncheckedAccount<'info>, #[account(constraint = instructions_program.key() == sysvar::instructions::id())] @@ -65,24 +54,27 @@ pub struct ExecuteTransferHook<'info> { } pub fn handler(ctx: Context, amount: u64) -> Result<()> { + let asset_mint = ctx.accounts.asset_mint.key(); + if ctx.accounts.destination_account.owner == get_asset_controller_account_pda(asset_mint) { + // if destination account is asset controller, skip enforcing token hook logic, since it is only used for revoking tokens + return Ok(()); + } + verify_cpi_program_is_token22( &ctx.accounts.instructions_program.to_account_info(), amount, - ctx.accounts.asset_mint.key(), + asset_mint, )?; - - let asset_mint = ctx.accounts.asset_mint.key(); - verify_pda( ctx.accounts.policy_engine_account.key(), &[&asset_mint.to_bytes()], - &policy_engine::id(), + &crate::id(), )?; verify_pda( ctx.accounts.policy_account.key(), &[&ctx.accounts.policy_engine_account.key().to_bytes()], - &policy_engine::id(), + &crate::id(), )?; // if policy account hasnt been created, skip enforcing token hook logic @@ -118,10 +110,8 @@ pub fn handler(ctx: Context, amount: u64) -> Result<()> { &identity_registry::id(), )?; - let levels = if let Ok(identity_account) = - IdentityAccount::deserialize(&mut &ctx.accounts.identity_account.data.borrow()[8..]) - { - identity_account.levels + let levels = if !ctx.accounts.identity_account.data_is_empty() { + IdentityAccount::deserialize(&mut &ctx.accounts.identity_account.data.borrow()[8..])?.levels } else { vec![NO_IDENTITY_LEVEL] }; @@ -131,6 +121,21 @@ pub fn handler(ctx: Context, amount: u64) -> Result<()> { return Ok(()); } + let mut tracker_account: Option = + if ctx.accounts.tracker_account.data_is_empty() { + None + } else { + Some(TrackerAccount::deserialize( + &mut &ctx.accounts.tracker_account.data.borrow()[8..], + )?) + }; + + let transfers = if let Some(tracker_account) = tracker_account.clone() { + tracker_account.transfers.clone() + } else { + vec![] + }; + // evaluate policies enforce_policy( policy_account.policies.clone(), @@ -138,15 +143,21 @@ pub fn handler(ctx: Context, amount: u64) -> Result<()> { Clock::get()?.unix_timestamp, &levels, ctx.accounts.destination_account.amount, - &ctx.accounts.tracker_account.transfers, + &transfers, )?; - // update transfer history - ctx.accounts.tracker_account.update_transfer_history( - amount, - Clock::get()?.unix_timestamp, - policy_engine_account.max_timeframe, - )?; + if let Some(ref mut tracker_account) = tracker_account { + // update transfer history + tracker_account.update_transfer_history( + amount, + Clock::get()?.unix_timestamp, + policy_engine_account.max_timeframe, + )?; + let tracker_account_data = tracker_account.try_to_vec()?; + let tracker_account_data_len = tracker_account_data.len(); + ctx.accounts.tracker_account.data.borrow_mut()[8..8 + tracker_account_data_len] + .copy_from_slice(&tracker_account_data); + } Ok(()) } diff --git a/programs/policy_engine/src/instructions/mod.rs b/programs/policy_engine/src/instructions/mod.rs index 6280ac6..ba8d39e 100644 --- a/programs/policy_engine/src/instructions/mod.rs +++ b/programs/policy_engine/src/instructions/mod.rs @@ -1,5 +1,9 @@ pub mod account; pub mod engine; +pub mod execute; +pub mod tracker; pub use account::*; pub use engine::*; +pub use execute::*; +pub use tracker::*; diff --git a/programs/policy_engine/src/instructions/tracker.rs b/programs/policy_engine/src/instructions/tracker.rs new file mode 100644 index 0000000..84a8ee5 --- /dev/null +++ b/programs/policy_engine/src/instructions/tracker.rs @@ -0,0 +1,39 @@ +use anchor_lang::prelude::*; +use anchor_spl::token_interface::Mint; + +use crate::TrackerAccount; + +#[derive(AnchorDeserialize, AnchorSerialize)] +pub struct CreateTokenAccountArgs { + pub memo_transfer: bool, +} + +#[derive(Accounts)] +#[instruction()] +#[event_cpi] +pub struct CreateTrackerAccount<'info> { + #[account(mut)] + pub payer: Signer<'info>, + #[account()] + /// CHECK: can be any account + pub owner: UncheckedAccount<'info>, + #[account()] + pub asset_mint: Box>, + #[account( + init, + space = 8 + TrackerAccount::INIT_SPACE, + seeds = [asset_mint.key().as_ref(), owner.key().as_ref()], + bump, + payer = payer, + )] + pub tracker_account: Box>, + pub system_program: Program<'info, System>, +} + +pub fn handler(ctx: Context) -> Result<()> { + ctx.accounts + .tracker_account + .new(ctx.accounts.asset_mint.key(), ctx.accounts.owner.key()); + + Ok(()) +} diff --git a/programs/policy_engine/src/lib.rs b/programs/policy_engine/src/lib.rs index 8c97c50..74494e3 100644 --- a/programs/policy_engine/src/lib.rs +++ b/programs/policy_engine/src/lib.rs @@ -15,7 +15,9 @@ use anchor_lang::prelude::*; declare_id!("po1cPf1eyUJJPqULw4so3T4JU9pdFn83CDyuLEKFAau"); #[program] +#[allow(deprecated)] pub mod policy_engine { + use super::*; /// create a policy registry @@ -53,4 +55,15 @@ pub mod policy_engine { ) -> Result<()> { instructions::account::detach::handler(ctx, hash) } + + /// create tracker account + pub fn create_tracker_account(ctx: Context) -> Result<()> { + instructions::tracker::handler(ctx) + } + + /// execute transfer hook + #[interface(spl_transfer_hook_interface::execute)] + pub fn execute_transaction(ctx: Context, amount: u64) -> Result<()> { + instructions::execute::handler(ctx, amount) + } } diff --git a/programs/policy_engine/src/state/engine.rs b/programs/policy_engine/src/state/engine.rs index 52d842e..59e4e77 100644 --- a/programs/policy_engine/src/state/engine.rs +++ b/programs/policy_engine/src/state/engine.rs @@ -59,3 +59,12 @@ impl PolicyEngineAccount { self.max_timeframe = max_timeframe; } } + +/// bec of circular dependancy acp's pubkey is hardcoded +pub fn get_asset_controller_account_pda(asset_mint: Pubkey) -> Pubkey { + Pubkey::find_program_address( + &[asset_mint.as_ref()], + &pubkey!("acpcFrzEYKjVLvZGWueTV8vyDjhu3oKC7sN38QELLan"), + ) + .0 +} diff --git a/programs/policy_engine/src/state/mod.rs b/programs/policy_engine/src/state/mod.rs index 2226fbb..dba15c1 100644 --- a/programs/policy_engine/src/state/mod.rs +++ b/programs/policy_engine/src/state/mod.rs @@ -1,8 +1,10 @@ pub mod account; pub mod engine; +pub mod track; pub use account::*; pub use engine::*; +pub use track::*; use anchor_lang::{solana_program::program_error::ProgramError, AnchorDeserialize, Discriminator}; use rwa_utils::GeyserProgramAccount; @@ -10,6 +12,7 @@ use rwa_utils::GeyserProgramAccount; pub enum PolicyEngineAccounts { PolicyAccount(PolicyAccount), PolicyEngineAccount(PolicyEngineAccount), + TrackerAccount(TrackerAccount), } impl GeyserProgramAccount for PolicyEngineAccounts { @@ -17,6 +20,7 @@ impl GeyserProgramAccount for PolicyEngineAccounts { match self { PolicyEngineAccounts::PolicyAccount(_) => PolicyAccount::DISCRIMINATOR, PolicyEngineAccounts::PolicyEngineAccount(_) => PolicyEngineAccount::DISCRIMINATOR, + PolicyEngineAccounts::TrackerAccount(_) => TrackerAccount::DISCRIMINATOR, } } @@ -42,6 +46,10 @@ impl GeyserProgramAccount for PolicyEngineAccounts { let account = PolicyEngineAccount::deserialize(account_data)?; Ok(PolicyEngineAccounts::PolicyEngineAccount(account)) } + TrackerAccount::DISCRIMINATOR => { + let account = TrackerAccount::deserialize(account_data)?; + Ok(PolicyEngineAccounts::TrackerAccount(account)) + } _ => Err(ProgramError::InvalidAccountData), } } diff --git a/programs/asset_controller/src/state/track.rs b/programs/policy_engine/src/state/track.rs similarity index 91% rename from programs/asset_controller/src/state/track.rs rename to programs/policy_engine/src/state/track.rs index deab5aa..b216a33 100644 --- a/programs/asset_controller/src/state/track.rs +++ b/programs/policy_engine/src/state/track.rs @@ -1,7 +1,6 @@ use anchor_lang::prelude::*; -use policy_engine::Transfer; -use crate::AssetControllerErrors; +use crate::{PolicyEngineErrors, Transfer}; pub const MAX_TRANSFER_HISTORY: usize = 25; @@ -45,7 +44,7 @@ impl TrackerAccount { self.transfers.push(Transfer { amount, timestamp }); // return error if the transfer history is too large if self.transfers.len() > MAX_TRANSFER_HISTORY { - return Err(AssetControllerErrors::TransferHistoryFull.into()); + return Err(PolicyEngineErrors::TransferHistoryFull.into()); } Ok(()) } diff --git a/programs/policy_engine/src/utils.rs b/programs/policy_engine/src/utils.rs index b1fea4a..2a66fd2 100644 --- a/programs/policy_engine/src/utils.rs +++ b/programs/policy_engine/src/utils.rs @@ -1,5 +1,12 @@ use crate::{state::*, PolicyEngineErrors}; -use anchor_lang::prelude::*; +use anchor_lang::{ + prelude::*, + solana_program::sysvar::{self, instructions::get_instruction_relative}, +}; +use anchor_spl::token_2022; +use spl_tlv_account_resolution::{ + account::ExtraAccountMeta, seeds::Seed, state::ExtraAccountMetaList, +}; pub fn enforce_identity_filter(identity: &[u8], identity_filter: IdentityFilter) -> Result<()> { match identity_filter.comparision_type { @@ -112,3 +119,90 @@ pub fn enforce_policy( } Ok(()) } + +pub const TRANSFER_HOOK_MINT_INDEX: usize = 1; + +pub fn verify_cpi_program_is_token22( + instructions_program: &AccountInfo, + amount: u64, + mint: Pubkey, +) -> Result<()> { + let ix_relative = get_instruction_relative(0, instructions_program)?; + if ix_relative.program_id != token_2022::ID { + return Err(PolicyEngineErrors::InvalidCpiTransferProgram.into()); + } + if ix_relative.data[1..9] != amount.to_le_bytes() { + return Err(PolicyEngineErrors::InvalidCpiTransferAmount.into()); + } + // make sure transfer mint is same + if let Some(account) = ix_relative.accounts.get(TRANSFER_HOOK_MINT_INDEX) { + if account.pubkey != mint { + return Err(PolicyEngineErrors::InvalidCpiTransferMint.into()); + } + } else { + return Err(PolicyEngineErrors::InvalidCpiTransferProgram.into()); + } + + Ok(()) +} + +#[inline(never)] +pub fn verify_pda(address: Pubkey, seeds: &[&[u8]], program_id: &Pubkey) -> Result<()> { + let (pda, _) = Pubkey::find_program_address(seeds, program_id); + if pda != address { + return Err(PolicyEngineErrors::InvalidPdaPassedIn.into()); + } + Ok(()) +} + +pub fn get_meta_list_size() -> Result { + Ok(ExtraAccountMetaList::size_of(get_extra_account_metas()?.len()).unwrap()) +} + +pub fn get_extra_account_metas() -> Result> { + Ok(vec![ + // policy engine account + ExtraAccountMeta::new_with_seeds(&[Seed::AccountKey { index: 1 }], false, false)?, + // identity program + ExtraAccountMeta::new_with_pubkey(&identity_registry::id(), false, false)?, + // identity registry account + ExtraAccountMeta::new_external_pda_with_seeds( + 6, + &[Seed::AccountKey { index: 1 }], + false, + false, + )?, + // user identity account + ExtraAccountMeta::new_external_pda_with_seeds( + 6, + &[ + Seed::AccountKey { index: 7 }, + Seed::AccountData { + // to pubkey + account_index: 2, // to token account + data_index: 32, + length: 32, + }, + ], + false, + false, + )?, + // user tracker account + ExtraAccountMeta::new_with_seeds( + &[ + Seed::AccountKey { index: 1 }, + Seed::AccountData { + account_index: 2, + data_index: 32, + length: 32, + }, + ], + false, + true, + )?, + // policy account + ExtraAccountMeta::new_with_seeds(&[Seed::AccountKey { index: 5 }], false, true)?, + // instructions program + ExtraAccountMeta::new_with_pubkey(&sysvar::instructions::id(), false, false)?, + ]) +} diff --git a/programs/rwa_utils/src/constants.rs b/programs/rwa_utils/src/constants.rs index ca425f6..84895bc 100644 --- a/programs/rwa_utils/src/constants.rs +++ b/programs/rwa_utils/src/constants.rs @@ -1,3 +1,4 @@ use anchor_lang::solana_program::pubkey::Pubkey; pub const TOKEN22: Pubkey = anchor_spl::token_2022::ID; +pub const META_LIST_ACCOUNT_SEED: &[u8] = b"extra-account-metas";