From d4cbc734227a9b2f24c56a35bcb85c77a96c81f8 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Thu, 23 Mar 2023 12:49:29 +0000 Subject: [PATCH 1/2] refactored user class, and added custom errors --- light-sdk-ts/src/constants.ts | 4 +- light-sdk-ts/src/errors.ts | 25 +- light-sdk-ts/src/test-utils/createAccounts.ts | 30 +- .../src/test-utils/setUpMerkleTree.ts | 22 +- light-sdk-ts/src/wallet/buildBalance.ts | 17 +- light-sdk-ts/src/wallet/createOutUtxos.ts | 34 +- light-sdk-ts/src/wallet/provider.ts | 14 +- light-sdk-ts/src/wallet/selectInUtxos.ts | 22 +- light-sdk-ts/src/wallet/user.ts | 586 +++++++++++------- light-sdk-ts/tests/createOutUtxos.test.ts | 92 +-- light-sdk-ts/tests/selectInUtxos.test.ts | 40 +- light-system-programs/package.json | 1 + light-system-programs/tests/user_tests.ts | 252 +++++++- light-system-programs/yarn.lock | 7 + 14 files changed, 758 insertions(+), 388 deletions(-) diff --git a/light-sdk-ts/src/constants.ts b/light-sdk-ts/src/constants.ts index 4c00ae6b0e..82a9761405 100644 --- a/light-sdk-ts/src/constants.ts +++ b/light-sdk-ts/src/constants.ts @@ -144,14 +144,14 @@ export const RELAYER_FEES = 1e6; export const TOKEN_REGISTRY = [ { symbol: "SOL", - decimals: 1e9, + decimals: new anchor.BN(1e9), isNft: false, // TODO: parse from onchain state at configuration(decimlas, supply) isSol: true, tokenAccount: SystemProgram.programId, }, { symbol: "USDC", - decimals: 1e2, + decimals: new anchor.BN(1e2), isNft: false, isSol: false, // copied from MINT (test-utils) diff --git a/light-sdk-ts/src/errors.ts b/light-sdk-ts/src/errors.ts index e00fb36877..1ac04ec494 100644 --- a/light-sdk-ts/src/errors.ts +++ b/light-sdk-ts/src/errors.ts @@ -1,3 +1,5 @@ +import { ASSOCIATED_PROGRAM_ID } from "@coral-xyz/anchor/dist/cjs/utils/token"; + export enum UtxoErrorCode { APP_DATA_FROM_BYTES_FUNCTION_UNDEFINED = "APP_DATA_FROM_BYTES_FUNCTION_UNDEFINED", INVALID_ASSET_OR_AMOUNTS_LENGTH = "INVALID_ASSET_OR_AMOUNTS_LENGTH", @@ -10,6 +12,23 @@ export enum UtxoErrorCode { ASSET_NOT_FOUND = "ASSET_NOT_FOUND", } +export enum UserErrorCode { + NO_WALLET_PROVIDED = "NO_WALLET_PROVIDED", + LOAD_ERROR = "LOAD_ERROR", + PROVIDER_NOT_INITIALIZED = "PROVIDER_NOT_INITIALIZED", + UTXOS_NOT_INITIALIZED = "UTXOS_NOT_INITIALIZED", + USER_ACCOUNT_NOT_INITIALIZED = "USER_ACCOUNT_NOT_INITIALIZED", + TOKEN_NOT_FOUND = "TOKEN_NOT_FOUND", + NO_AMOUNTS_PROVIDED = "NO_AMOUNTS_PROVIDED", + APPROVE_ERROR = "APPROVE_ERROR", + INSUFFICIENT_BAlANCE = "INSUFFICIENT_BAlANCE", + ASSOCIATED_TOKEN_ACCOUNT_DOESNT_EXIST = "ASSOCIATED_TOKEN_ACCOUNT_DOESNT_EXIST", + TOKEN_ACCOUNT_DEFINED = "TOKEN_ACCOUNT_DEFINED", + SHIELDED_RECIPIENT_UNDEFINED = "SHIELDED_RECIPIENT_UNDEFINED", + TOKEN_UNDEFINED = "TOKEN_UNDEFINED", + INVALID_TOKEN = "INVALID_TOKEN", +} + export enum SelectInUtxosErrorCode { INVALID_NUMER_OF_MINTS = "INVALID_NUMER_OF_MINTS", FAILED_TO_SELECT_SOL_UTXO = "FAILED_TO_SELECT_SOL_UTXO", @@ -27,8 +46,8 @@ export enum CreateUtxoErrorCode { INVALID_NUMER_OF_RECIPIENTS = "INVALID_NUMER_OF_RECIPIENTS", INVALID_RECIPIENT_MINT = "INVALID_RECIPIENT_MINT", RECIPIENTS_SUM_AMOUNT_MISSMATCH = "RECIPIENTS_SUM_AMOUNT_MISSMATCH", - NO_PUBLIC_AMOUNTS_PROVIDED = "NO_PUBLIC_AMOUNTS_PROVIDED_FOR_UNSHIELD", - NO_PUBLIC_MINT_PROVIDED = "NO_PUBLIC_MINT_PROVIDED_FOR_UNSHIELD", + NO_PUBLIC_AMOUNTS_PROVIDED = "NO_PUBLIC_AMOUNTS_PROVIDED", + NO_PUBLIC_MINT_PROVIDED = "NO_PUBLIC_MINT_PROVIDED", MINT_UNDEFINED = "MINT_UNDEFINED", SPL_AMOUNT_UNDEFINED = "SPL_AMOUNT_UNDEFINED", ACCOUNT_UNDEFINED = "ACCOUNT_UNDEFINED", @@ -165,3 +184,5 @@ export class CreateUtxoError extends MetaError {} export class ProviderError extends MetaError {} export class SelectInUtxosError extends MetaError {} + +export class UserError extends MetaError {} diff --git a/light-sdk-ts/src/test-utils/createAccounts.ts b/light-sdk-ts/src/test-utils/createAccounts.ts index a119a21245..a6a4644c2e 100644 --- a/light-sdk-ts/src/test-utils/createAccounts.ts +++ b/light-sdk-ts/src/test-utils/createAccounts.ts @@ -243,14 +243,9 @@ export async function createTestAccounts( let balance = await connection.getBalance(ADMIN_AUTH_KEY, "confirmed"); if (balance === 0) { let amount = 1_000_000_000_000; - console.time("requestAirdrop"); let res = await connection.requestAirdrop(ADMIN_AUTH_KEY, amount); - console.timeEnd("requestAirdrop"); - console.time("confirmAirdrop"); - await connection.confirmTransaction(res, "confirmed"); - console.timeEnd("confirmAirdrop"); let Newbalance = await connection.getBalance(ADMIN_AUTH_KEY); @@ -287,7 +282,10 @@ export async function createTestAccounts( mintKeypair: Keypair.fromSecretKey(MINT_PRIVATE_KEY), connection, }); - console.log("created mint"); + console.log( + "created mint ", + Keypair.fromSecretKey(MINT_PRIVATE_KEY).publicKey.toBase58(), + ); } let balanceUserToken = null; @@ -330,17 +328,17 @@ export async function createTestAccounts( } catch (error) { console.log(error); } - console.log("userSplAccount ", userSplAccount); + console.log("userSplAccount ", userSplAccount?.toBase58()); - console.log( - "funded account", - await getAccount( - connection, - userSplAccount!, //userTokenAccount, - "confirmed", - TOKEN_PROGRAM_ID, - ), - ); + // console.log( + // "funded account", + // await getAccount( + // connection, + // userSplAccount!, //userTokenAccount, + // "confirmed", + // TOKEN_PROGRAM_ID, + // ), + // ); try { if (balanceUserToken == null) { diff --git a/light-sdk-ts/src/test-utils/setUpMerkleTree.ts b/light-sdk-ts/src/test-utils/setUpMerkleTree.ts index 14970952f4..27f5f4cfcc 100644 --- a/light-sdk-ts/src/test-utils/setUpMerkleTree.ts +++ b/light-sdk-ts/src/test-utils/setUpMerkleTree.ts @@ -58,16 +58,16 @@ export async function setUpMerkleTree(provider: anchor.Provider) { console.log(e); } - console.log("AUTHORITY: ", AUTHORITY); + // console.log("AUTHORITY: ", AUTHORITY); - console.log("AUTHORITY: ", Array.from(AUTHORITY.toBytes())); + // console.log("AUTHORITY: ", Array.from(AUTHORITY.toBytes())); - console.log( - "verifierProgramZero.programId: ", - Array.from(verifierProgramZero.programId.toBytes()), - ); - console.log("MERKLE_TREE_KEY: ", MERKLE_TREE_KEY.toBase58()); - console.log("MERKLE_TREE_KEY: ", Array.from(MERKLE_TREE_KEY.toBytes())); + // console.log( + // "verifierProgramZero.programId: ", + // Array.from(verifierProgramZero.programId.toBytes()), + // ); + // console.log("MERKLE_TREE_KEY: ", MERKLE_TREE_KEY.toBase58()); + // console.log("MERKLE_TREE_KEY: ", Array.from(MERKLE_TREE_KEY.toBytes())); // console.log("MERKLE_TREE_PDA_TOKEN: ", MERKLE_TREE_PDA_TOKEN.toBase58()) // console.log("MERKLE_TREE_PDA_TOKEN: ", Array.from(MERKLE_TREE_PDA_TOKEN.toBytes())) @@ -100,7 +100,7 @@ export async function setUpMerkleTree(provider: anchor.Provider) { try { await merkleTreeConfig.registerVerifier(verifierProgramTwo.programId); - console.log("Registering Verifier One success"); + console.log("Registering Verifier Two success"); } catch (e) { console.log(e); } @@ -112,8 +112,8 @@ export async function setUpMerkleTree(provider: anchor.Provider) { console.log(e); } - console.log("MINT: ", MINT.toBase58()); - console.log("POOL_TYPE_PDA: ", REGISTERED_POOL_PDA_SPL.toBase58()); + // console.log("MINT: ", MINT.toBase58()); + // console.log("POOL_TYPE_PDA: ", REGISTERED_POOL_PDA_SPL.toBase58()); try { await merkleTreeConfig.registerSplPool(POOL_TYPE, MINT); console.log("Registering spl pool success"); diff --git a/light-sdk-ts/src/wallet/buildBalance.ts b/light-sdk-ts/src/wallet/buildBalance.ts index c912db516b..e307250225 100644 --- a/light-sdk-ts/src/wallet/buildBalance.ts +++ b/light-sdk-ts/src/wallet/buildBalance.ts @@ -131,13 +131,13 @@ export async function getUnspentUtxos({ let accountInfo = await provider.connection.getAccountInfo( nullifierPubkey, ); - console.log( - "inserted -- spent?", - accountInfo ? "yes" : "no ", - nullifierPubkey.toBase58(), - "amount:", - decryptedUtxo.amounts[0].toNumber(), - ); + // console.log( + // "inserted -- spent?", + // accountInfo ? "yes" : "no ", + // nullifierPubkey.toBase58(), + // "amount:", + // decryptedUtxo.amounts[0].toNumber(), + // ); if ( !accountInfo && (decryptedUtxo.amounts[1].toString() !== "0" || @@ -147,9 +147,6 @@ export async function getUnspentUtxos({ } } } - if (decryptedUtxos.length == 0) { - console.log("no unspent leaf found"); - } return decryptedUtxos; } diff --git a/light-sdk-ts/src/wallet/createOutUtxos.ts b/light-sdk-ts/src/wallet/createOutUtxos.ts index 6a83a36efe..42baec7ede 100644 --- a/light-sdk-ts/src/wallet/createOutUtxos.ts +++ b/light-sdk-ts/src/wallet/createOutUtxos.ts @@ -80,8 +80,8 @@ export function createOutUtxos({ poseidon, inUtxos, publicMint, - publicSplAmount, - publicSolAmount, + publicAmountSpl, + publicAmountSol, relayerFee, changeUtxoAccount, recipients = [], @@ -89,8 +89,8 @@ export function createOutUtxos({ }: { inUtxos?: Utxo[]; publicMint?: PublicKey; - publicSplAmount?: BN; - publicSolAmount?: BN; + publicAmountSpl?: BN; + publicAmountSol?: BN; relayerFee?: BN; poseidon: any; changeUtxoAccount: Account; @@ -105,8 +105,8 @@ export function createOutUtxos({ ); if (relayerFee) { - publicSolAmount = publicSolAmount - ? publicSolAmount.add(relayerFee) + publicAmountSol = publicAmountSol + ? publicAmountSol.add(relayerFee) : relayerFee; } @@ -192,20 +192,20 @@ export function createOutUtxos({ // subtract public amounts from sumIns if (action === Action.UNSHIELD) { - if (!publicSolAmount && !publicSplAmount) + if (!publicAmountSol && !publicAmountSpl) throw new CreateUtxoError( CreateUtxoErrorCode.NO_PUBLIC_AMOUNTS_PROVIDED, "createOutUtxos", - "publicSolAmount not initialized for unshield", + "publicAmountSol not initialized for unshield", ); - if (!publicSplAmount) publicSplAmount = new BN(0); - if (!publicSolAmount) + if (!publicAmountSpl) publicAmountSpl = new BN(0); + if (!publicAmountSol) throw new CreateUtxoError( CreateUtxoErrorCode.PUBLIC_SOL_AMOUNT_UNDEFINED, "constructor", ); - if (publicSplAmount && !publicMint) + if (publicAmountSpl && !publicMint) throw new CreateUtxoError( CreateUtxoErrorCode.NO_PUBLIC_MINT_PROVIDED, "createOutUtxos", @@ -217,9 +217,9 @@ export function createOutUtxos({ ); assets[publicSplAssetIndex].sumIn = - assets[publicSplAssetIndex].sumIn.sub(publicSplAmount); + assets[publicSplAssetIndex].sumIn.sub(publicAmountSpl); assets[publicSolAssetIndex].sumIn = - assets[publicSolAssetIndex].sumIn.sub(publicSolAmount); + assets[publicSolAssetIndex].sumIn.sub(publicAmountSol); // add public amounts to sumIns } else if (action === Action.SHIELD) { if (relayerFee) @@ -243,11 +243,11 @@ export function createOutUtxos({ (x) => x.asset.toBase58() === SystemProgram.programId.toBase58(), ); assets[publicSplAssetIndex].sumIn = - assets[publicSplAssetIndex].sumIn.add(publicSplAmount); + assets[publicSplAssetIndex].sumIn.add(publicAmountSpl); assets[publicSolAssetIndex].sumIn = - assets[publicSolAssetIndex].sumIn.add(publicSolAmount); + assets[publicSolAssetIndex].sumIn.add(publicAmountSol); } else if (action === Action.TRANSFER) { - if (!publicSolAmount) + if (!publicAmountSol) throw new CreateUtxoError( CreateUtxoErrorCode.PUBLIC_SOL_AMOUNT_UNDEFINED, "constructor", @@ -257,7 +257,7 @@ export function createOutUtxos({ ); assets[publicSolAssetIndex].sumIn = - assets[publicSolAssetIndex].sumIn.sub(publicSolAmount); + assets[publicSolAssetIndex].sumIn.sub(publicAmountSol); } var outputUtxos: Utxo[] = []; diff --git a/light-sdk-ts/src/wallet/provider.ts b/light-sdk-ts/src/wallet/provider.ts index 9bca296713..eaa4c3c4ab 100644 --- a/light-sdk-ts/src/wallet/provider.ts +++ b/light-sdk-ts/src/wallet/provider.ts @@ -1,4 +1,10 @@ -import { AnchorProvider, setProvider } from "@coral-xyz/anchor"; +import { + AnchorError, + AnchorProvider, + BN, + setProvider, + Wallet, +} from "@coral-xyz/anchor"; import { SolMerkleTree } from "../merkleTree"; import { PublicKey, @@ -37,8 +43,8 @@ export class Provider { solMerkleTree?: SolMerkleTree; provider?: AnchorProvider | { connection: Connection }; // temp -? url?: string; - // TODO: refactor by streamlining everything towards using connection and not anchor provider - // - get rid of url + minimumLamports: BN; + /** * Init either with nodeWallet or browserWallet. Feepayer is the provided wallet * Optionally provide confirmConfig, Default = 'confirmed'. @@ -49,12 +55,14 @@ export class Provider { confirmConfig, connection, url = "http://127.0.0.1:8899", + minimumLamports = new BN(5000 * 30), }: { nodeWallet?: SolanaKeypair; browserWallet?: BrowserWallet; confirmConfig?: ConfirmOptions; connection?: Connection; url?: string; + minimumLamports?: BN; }) { if (nodeWallet && browserWallet) throw new ProviderError( diff --git a/light-sdk-ts/src/wallet/selectInUtxos.ts b/light-sdk-ts/src/wallet/selectInUtxos.ts index 3e477b57f1..4450315d74 100644 --- a/light-sdk-ts/src/wallet/selectInUtxos.ts +++ b/light-sdk-ts/src/wallet/selectInUtxos.ts @@ -135,33 +135,33 @@ const selectBiggestSmallest = ( export function selectInUtxos({ utxos, publicMint, - publicSplAmount, - publicSolAmount, + publicAmountSpl, + publicAmountSol, relayerFee, recipients = [], action, }: { publicMint?: PublicKey; - publicSplAmount?: BN; - publicSolAmount?: BN; + publicAmountSpl?: BN; + publicAmountSol?: BN; relayerFee?: BN; utxos?: Utxo[]; recipients?: Recipient[]; action: Action; }) { - if (!publicMint && publicSplAmount) + if (!publicMint && publicAmountSpl) throw new SelectInUtxosError( CreateUtxoErrorCode.NO_PUBLIC_MINT_PROVIDED, "selectInUtxos", "Public mint not set but public spl amount", ); - if (publicMint && !publicSplAmount) + if (publicMint && !publicAmountSpl) throw new SelectInUtxosError( CreateUtxoErrorCode.PUBLIC_SPL_AMOUNT_UNDEFINED, "selectInUtxos", "Public spl amount not set but public mint", ); - if (action === Action.UNSHIELD && !publicSplAmount && !publicSolAmount) + if (action === Action.UNSHIELD && !publicAmountSpl && !publicAmountSol) throw new SelectInUtxosError( CreateUtxoErrorCode.NO_PUBLIC_AMOUNTS_PROVIDED, "selectInUtxos", @@ -196,8 +196,8 @@ export function selectInUtxos({ ); // TODO: evaluate whether this is too much of a footgun if (action === Action.SHIELD) { - publicSolAmount = new BN(0); - publicSplAmount = new BN(0); + publicAmountSol = new BN(0); + publicAmountSpl = new BN(0); } // TODO: add check that utxo holds sufficient balance @@ -228,10 +228,10 @@ export function selectInUtxos({ let filteredUtxos: Utxo[] = []; var sumInSpl = new BN(0); var sumInSol = getUtxoArrayAmount(SystemProgram.programId, utxos); - var sumOutSpl = publicSplAmount ? publicSplAmount : new BN(0); + var sumOutSpl = publicAmountSpl ? publicAmountSpl : new BN(0); var sumOutSol = getRecipientsAmount(SystemProgram.programId, recipients); if (relayerFee) sumOutSol = sumOutSol.add(relayerFee); - if (publicSolAmount) sumOutSol = sumOutSol.add(publicSolAmount); + if (publicAmountSol) sumOutSol = sumOutSol.add(publicAmountSol); if (mint) { filteredUtxos = utxos.filter((utxo) => utxo.assets.includes(mint!)); diff --git a/light-sdk-ts/src/wallet/user.ts b/light-sdk-ts/src/wallet/user.ts index 737f55372d..32ac1b7c8e 100644 --- a/light-sdk-ts/src/wallet/user.ts +++ b/light-sdk-ts/src/wallet/user.ts @@ -44,18 +44,27 @@ import axios from "axios"; import { selectInUtxos } from "./selectInUtxos"; import { createOutUtxos, Recipient } from "./createOutUtxos"; import { strToArr } from "../utils"; +import { + CreateUtxoErrorCode, + ProviderErrorCode, + RelayerErrorCode, + TransactionErrorCode, + TransactionParametersErrorCode, + UserError, + UserErrorCode, +} from "../errors"; const message = new TextEncoder().encode(SIGN_MESSAGE); type Balance = { symbol: string; - amount: number; + amount: BN; tokenAccount: PublicKey; - decimals: number; + decimals: BN; }; type TokenContext = { symbol: string; - decimals: number; + decimals: BN; tokenAccount: PublicKey; isNft: boolean; isSol: boolean; @@ -65,7 +74,12 @@ export type CachedUserState = { utxos: Utxo[]; seed: string; }; - +export const convertAndComputeDecimals = ( + amount: BN | string | number, + decimals: BN, +) => { + return new BN(amount.toString()).mul(decimals); +}; var initLog = console.log; // TODO: Utxos should be assigned to a merkle tree @@ -95,10 +109,18 @@ export class User { account?: Account; }) { if (!provider.browserWallet && !provider.nodeWallet) - throw new Error("No wallet provided"); + throw new UserError( + UserErrorCode.NO_WALLET_PROVIDED, + "constructor", + "No wallet provided", + ); if (!provider.lookUpTable || !provider.solMerkleTree || !provider.poseidon) - throw new Error("Provider not properly initialized"); + throw new UserError( + UserErrorCode.PROVIDER_NOT_INITIALIZED, + "constructor", + "Provider not properly initialized", + ); this.provider = provider; this.utxos = utxos; @@ -111,22 +133,47 @@ export class User { latest?: boolean; }): Promise { const balances: Balance[] = []; - if (!this.utxos) throw new Error("Utxos not initialized"); - if (!this.account) throw new Error("Keypair not initialized"); - if (!this.provider) throw new Error("Provider not initialized"); - if (!this.provider.poseidon) throw new Error("Poseidon not initialized"); + if (!this.utxos) + throw new UserError( + UserErrorCode.UTXOS_NOT_INITIALIZED, + "getBalances", + "Utxos not initialized", + ); + if (!this.account) + throw new UserError( + UserErrorCode.UTXOS_NOT_INITIALIZED, + "getBalances", + "Account not initialized", + ); + if (!this.provider) + throw new UserError( + UserErrorCode.USER_ACCOUNT_NOT_INITIALIZED, + "Provider not initialized", + ); + if (!this.provider.poseidon) + throw new UserError( + TransactionParametersErrorCode.NO_POSEIDON_HASHER_PROVIDED, + "Poseidon not initialized", + ); if (!this.provider.solMerkleTree) - throw new Error("Merkle Tree not initialized"); + throw new UserError( + ProviderErrorCode.SOL_MERKLE_TREE_UNDEFINED, + "getBalance", + "Merkle Tree not initialized", + ); if (!this.provider.lookUpTable) - throw new Error("Look up table not initialized"); + throw new UserError( + RelayerErrorCode.LOOK_UP_TABLE_UNDEFINED, + "getBalance", + "Look up table not initialized", + ); - // try { if (latest) { let leavesPdas = await SolMerkleTree.getInsertedLeaves( MERKLE_TREE_KEY, this.provider.provider, ); - console.log("leaves pdas", leavesPdas.length); + //TODO: add: "pending" to balances //TODO: add init by cached (subset of leavesPdas) const params = { @@ -149,7 +196,7 @@ export class User { TOKEN_REGISTRY.forEach((token) => { balances.push({ symbol: token.symbol, - amount: 0, + amount: new BN(0), tokenAccount: token.tokenAccount, decimals: token.decimals, }); @@ -158,20 +205,22 @@ export class User { this.utxos.forEach((utxo) => { utxo.assets.forEach((asset, i) => { const tokenAccount = asset; - const amount = utxo.amounts[i].toNumber(); + const amount = utxo.amounts[i]; const existingBalance = balances.find( (balance) => balance.tokenAccount.toBase58() === tokenAccount.toBase58(), ); if (existingBalance) { - existingBalance.amount += amount; + existingBalance.amount = existingBalance.amount.add(amount); } else { let tokenData = TOKEN_REGISTRY.find( (t) => t.tokenAccount.toBase58() === tokenAccount.toBase58(), ); if (!tokenData) - throw new Error( + throw new UserError( + UserErrorCode.TOKEN_NOT_FOUND, + "getBalance", `Token ${tokenAccount.toBase58()} not found in registry`, ); balances.push({ @@ -185,9 +234,6 @@ export class User { }); // TODO: add "pending" balances, return balances; - // } catch (err) { - // throw new Error(`Èrror in getting the user balance: ${err.message}`); - // } } async getRelayer( @@ -218,8 +264,8 @@ export class User { // TODO: in UI, support wallet switching, "prefill option with button" async getTxParams({ tokenCtx, - publicSplAmount, - publicSolAmount, + publicAmountSpl, + publicAmountSol, action, userSplAccount = AUTHORITY, // for unshield @@ -227,84 +273,68 @@ export class User { recipientSPLAddress, // for transfer shieldedRecipients, + ataCreationFee, }: { tokenCtx: TokenContext; - publicSplAmount?: BN; - publicSolAmount?: BN; + publicAmountSpl?: BN; + publicAmountSol?: BN; userSplAccount?: PublicKey; recipientFee?: PublicKey; recipientSPLAddress?: PublicKey; shieldedRecipients?: Recipient[]; action: Action; + ataCreationFee?: boolean; }): Promise { var relayer; - if (action === Action.SHIELD) { - if (!publicSolAmount && !publicSplAmount) - throw new Error( - "No public amount provided. Shield needs a public amount.", - ); - } else if (action === Action.UNSHIELD) { - if (!recipientFee) - throw new Error("no recipient provided for sol unshield"); - - let ataCreationFee = false; - - if (!tokenCtx.isSol) { - if (!recipientSPLAddress) - throw new Error("no recipient SPL address provided for unshield"); - let tokenBalance = - await this.provider.connection?.getTokenAccountBalance( - recipientSPLAddress, - ); - if (!tokenBalance?.value.uiAmount) { - /** Signal relayer to create the ATA and charge an extra fee for it */ - ataCreationFee = true; - } - } - + if (action === Action.TRANSFER || action === Action.UNSHIELD) { const { relayer: _relayer, feeRecipient: _feeRecipient } = await this.getRelayer(ataCreationFee); relayer = _relayer; - } else if (action === Action.TRANSFER) { - if (!shieldedRecipients || shieldedRecipients.length === 0) - throw new Error("no recipient provided for unshield"); - const { relayer: _relayer, feeRecipient: _feeRecipient } = - await this.getRelayer(); - relayer = _relayer; - } else throw new Error("Invalid action"); - - publicSolAmount = publicSolAmount ? publicSolAmount : new BN(0); - publicSplAmount = publicSplAmount ? publicSplAmount : new BN(0); + } - var inputUtxos: Utxo[] = []; - var outputUtxos: Utxo[] = []; + publicAmountSol = publicAmountSol ? publicAmountSol : new BN(0); + publicAmountSpl = publicAmountSpl ? publicAmountSpl : new BN(0); if (action === Action.TRANSFER && !shieldedRecipients) - throw new Error( - "Recipient or RecipientEncryptionPublicKey not provided for transfer", + throw new UserError( + UserErrorCode.SHIELDED_RECIPIENT_UNDEFINED, + "getTxParams", + "Recipient not provided for transfer", ); + if (action !== Action.SHIELD && !relayer?.relayerFee) // TODO: could make easier to read by adding separate if/cases - throw new Error(`No relayerFee provided for ${action.toLowerCase()}}`); + throw new UserError( + RelayerErrorCode.RELAYER_FEE_UNDEFINED, + "getTxParams", + `No relayerFee provided for ${action.toLowerCase()}}`, + ); + if (!this.account) { + throw new UserError( + CreateUtxoErrorCode.ACCOUNT_UNDEFINED, + "getTxParams", + "Account not defined", + ); + } + + var inputUtxos: Utxo[] = []; + var outputUtxos: Utxo[] = []; inputUtxos = selectInUtxos({ publicMint: tokenCtx.tokenAccount, - publicSplAmount, - publicSolAmount, + publicAmountSpl, + publicAmountSol, recipients: shieldedRecipients, utxos: this.utxos, relayerFee: relayer?.relayerFee, action, }); - if (!this.account) { - throw new Error("Account not defined"); - } outputUtxos = createOutUtxos({ publicMint: tokenCtx.tokenAccount, - publicSplAmount, + publicAmountSpl, inUtxos: inputUtxos, - publicSolAmount, // TODO: add support for extra sol for unshield & transfer + publicAmountSol, // TODO: add support for extra sol for unshield & transfer poseidon: this.provider.poseidon, relayerFee: relayer?.relayerFee, changeUtxoAccount: this.account, @@ -332,124 +362,169 @@ export class User { return txParams; } - // TODO: make inputs for amount string || BN || number -> then convert into a BN with new BN(amount.toString()) /** * * @param amount e.g. 1 SOL = 1, 2 USDC = 2 * @param token "SOL", "USDC", "USDT", * @param recipient optional, if not set, will shield to self * @param extraSolAmount optional, if set, will add extra SOL to the shielded amount - * @param userTokenAccount optional, if set, will use this token account to shield from, else derives ATA + * @param senderTokenAccount optional, if set, will use this token account to shield from, else derives ATA */ async shield({ token, - amount, + publicAmountSpl, recipient, - extraSolAmount, - userTokenAccount, + publicAmountSol, + senderTokenAccount, + minimumLamports = true, }: { token: string; - amount: number; - recipient?: anchor.BN; - extraSolAmount?: number; - userTokenAccount?: PublicKey; + recipient?: Account; + publicAmountSpl?: number | BN | string; + publicAmountSol?: number | BN | string; + minimumLamports?: boolean; + senderTokenAccount?: PublicKey; }) { - if (!this.provider) throw new Error("Provider not set!"); + if (publicAmountSpl && token === "SOL") + throw new UserError( + UserErrorCode.INVALID_TOKEN, + "shield", + "No public amount provided. Shield needs a public amount.", + ); + if (!publicAmountSpl && !publicAmountSol) + throw new UserError( + CreateUtxoErrorCode.NO_PUBLIC_AMOUNTS_PROVIDED, + "shield", + "No public amounts provided. Shield needs a public amount.", + ); + + if (publicAmountSpl && !token) + throw new UserError( + UserErrorCode.TOKEN_UNDEFINED, + "shield", + "No public amounts provided. Shield needs a public amount.", + ); + + if (!this.provider) + throw new UserError( + UserErrorCode.PROVIDER_NOT_INITIALIZED, + "shield", + "Provider not set!", + ); if (recipient) throw new Error("Shields to other users not implemented yet!"); let tokenCtx = TOKEN_REGISTRY.find((t) => t.symbol === token); - if (!tokenCtx) throw new Error("Token not supported!"); - if (tokenCtx.isSol && userTokenAccount) - throw new Error("Cannot use userTokenAccount for SOL!"); - let userSplAccount = null; - if (!tokenCtx.isSol) { - if (this.provider.nodeWallet) { - if (userTokenAccount) { - userSplAccount = userTokenAccount; - } else { - userSplAccount = splToken.getAssociatedTokenAddressSync( - tokenCtx!.tokenAccount, - this.provider!.nodeWallet!.publicKey, - ); - } + if (!tokenCtx) + throw new UserError( + UserErrorCode.TOKEN_NOT_FOUND, + "shield", + "Token not supported!", + ); + if (tokenCtx.isSol && senderTokenAccount) + throw new UserError( + UserErrorCode.TOKEN_ACCOUNT_DEFINED, + "shield", + "Cannot use senderTokenAccount for SOL!", + ); + let userSplAccount = undefined; + publicAmountSpl = publicAmountSpl + ? convertAndComputeDecimals(publicAmountSpl, tokenCtx.decimals) + : undefined; + + // if no sol amount by default min amount if disabled 0 + publicAmountSol = publicAmountSol + ? convertAndComputeDecimals(publicAmountSol, new BN(1e9)) + : minimumLamports + ? this.provider.minimumLamports + : new BN(0); + + // TODO: refactor this is ugly + if (!tokenCtx.isSol && publicAmountSpl) { + if (senderTokenAccount) { + userSplAccount = senderTokenAccount; + } else { + userSplAccount = splToken.getAssociatedTokenAddressSync( + tokenCtx!.tokenAccount, + this.provider!.nodeWallet!.publicKey, + ); + } - amount = amount * tokenCtx.decimals; + let tokenBalance = await splToken.getAccount( + this.provider.provider?.connection!, + userSplAccount, + ); - let tokenBalance = await splToken.getAccount( - this.provider.provider?.connection!, - userSplAccount, + if (!tokenBalance) + throw new UserError( + UserErrorCode.ASSOCIATED_TOKEN_ACCOUNT_DOESNT_EXIST, + "shield", + "AssociatdTokenAccount doesn't exist!", ); - if (!tokenBalance) throw new Error("ATA doesn't exist!"); - - if (amount >= tokenBalance.amount) - throw new Error( - `Insufficient token balance! ${amount} bal: ${tokenBalance! - .amount!}`, - ); + if (publicAmountSpl.gte(new BN(tokenBalance.amount.toString()))) + throw new UserError( + UserErrorCode.INSUFFICIENT_BAlANCE, + "shield", + `Insufficient token balance! ${publicAmountSpl.toString()} bal: ${tokenBalance! + .amount!}`, + ); - try { - await splToken.approve( - this.provider.provider!.connection, - this.provider.nodeWallet!, - userSplAccount, //userTokenAccount, - AUTHORITY, //TODO: make dynamic based on verifier - this.provider.nodeWallet!, //USER_TOKEN_ACCOUNT, // owner2 - amount, - [this.provider.nodeWallet!], - ); - } catch (e) { - throw new Error(`Error approving token transfer! ${e}`); - } - } else { - // TODO: implement browserWallet support; for UI - throw new Error("Browser wallet support not implemented yet!"); + try { + await splToken.approve( + this.provider.provider!.connection, + this.provider.nodeWallet!, + userSplAccount, //tokenAccount, + AUTHORITY, //TODO: make dynamic based on verifier + this.provider.nodeWallet!, //USER_TOKEN_ACCOUNT, // owner2 + // change to bigint + publicAmountSpl.toNumber(), + [this.provider.nodeWallet!], + ); + } catch (e) { + throw new UserError( + UserErrorCode.APPROVE_ERROR, + "shield", + `Error approving token transfer! ${e}`, + ); } - extraSolAmount = extraSolAmount ? extraSolAmount * 1e9 : MINIMUM_LAMPORTS; - } else { - // amount = amount * tokenCtx.decimals; - extraSolAmount = amount * tokenCtx.decimals; - amount = 0; } const txParams = await this.getTxParams({ tokenCtx, - publicSplAmount: new BN(amount), action: Action.SHIELD, - publicSolAmount: new BN(extraSolAmount), - // @ts-ignore + publicAmountSol, + publicAmountSpl, userSplAccount, }); - // TODO: add browserWallet support let tx = new Transaction({ provider: this.provider, params: txParams, }); await tx.compileAndProve(); - + let txHash; try { - let res = await tx.sendAndConfirmTransaction(); - console.log( - `https://explorer.solana.com/tx/${res}?cluster=custom&customUrl=http%3A%2F%2Flocalhost%3A8899`, - ); + txHash = await tx.sendAndConfirmTransaction(); } catch (e) { - throw new Error(`Error in tx.sendAndConfirmTransaction! ${e}`); + throw new UserError( + TransactionErrorCode.SEND_TRANSACTION_FAILED, + "shield", + `Error in tx.sendAndConfirmTransaction! ${e}`, + ); } - // console.log = () => {}; - await tx.checkBalances(); - console.log = initLog; - console.log("✔️ checkBalances success!"); + let response; if (this.provider.browserWallet) { - const response = await axios.post( - "http://localhost:3331/updatemerkletree", - ); - console.log({ response }); + response = await axios.post("http://localhost:3331/updatemerkletree"); } + + return { txHash, response }; } + // TODO: add unshieldSol and unshieldSpl + // TODO: add optional passs-in token mint + // TODO: add pass-in tokenAccount /** * @params token: string * @params amount: number - in base units (e.g. lamports for 'SOL') @@ -458,42 +533,88 @@ export class User { */ async unshield({ token, - amount, - recipient, - extraSolAmount, + publicAmountSpl, + recipientSpl = new PublicKey(0), + publicAmountSol, + recipientSol, + minimumLamports = true, }: { token: string; - amount: number; - recipient: PublicKey; - extraSolAmount?: number; + recipientSpl?: PublicKey; + recipientSol?: PublicKey; + publicAmountSpl?: number | BN | string; + publicAmountSol?: number | BN | string; + minimumLamports?: boolean; }) { const tokenCtx = TOKEN_REGISTRY.find((t) => t.symbol === token); - if (!tokenCtx) throw new Error("Token not supported!"); + if (!tokenCtx) + throw new UserError( + UserErrorCode.TOKEN_NOT_FOUND, + "shield", + "Token not supported!", + ); - let recipientSPLAddress: PublicKey = new PublicKey(0); - amount = amount * tokenCtx.decimals; + if (!publicAmountSpl && !publicAmountSol) + throw new UserError( + CreateUtxoErrorCode.NO_PUBLIC_AMOUNTS_PROVIDED, + "unshield", + "Need to provide at least one amount for an unshield", + ); + if (publicAmountSol && !recipientSol) + throw new UserError( + TransactionErrorCode.SOL_RECIPIENT_UNDEFINED, + "getTxParams", + "no recipient provided for sol unshield", + ); + if ( + publicAmountSpl && + recipientSpl.toBase58() == new PublicKey(0).toBase58() + ) + throw new UserError( + TransactionErrorCode.SPL_RECIPIENT_UNDEFINED, + "getTxParams", + "no recipient provided for spl unshield", + ); - if (!tokenCtx.isSol) { - recipientSPLAddress = splToken.getAssociatedTokenAddressSync( + let ataCreationFee = false; + + if (!tokenCtx.isSol && publicAmountSpl) { + let tokenBalance = await this.provider.connection?.getTokenAccountBalance( + recipientSpl, + ); + if (!tokenBalance?.value.uiAmount) { + /** Signal relayer to create the ATA and charge an extra fee for it */ + ataCreationFee = true; + } + recipientSpl = splToken.getAssociatedTokenAddressSync( tokenCtx!.tokenAccount, - recipient, + recipientSpl, ); + } - extraSolAmount = extraSolAmount ? extraSolAmount * 1e9 : MINIMUM_LAMPORTS; - } else { - extraSolAmount = amount; - amount = 0; + var _publicSplAmount: BN | undefined = undefined; + if (publicAmountSpl) { + _publicSplAmount = convertAndComputeDecimals( + publicAmountSpl, + tokenCtx.decimals, + ); } + // if no sol amount by default min amount if disabled 0 + const _publicSolAmount = publicAmountSol + ? convertAndComputeDecimals(publicAmountSol, new BN(1e9)) + : minimumLamports + ? this.provider.minimumLamports + : new BN(0); + const txParams = await this.getTxParams({ tokenCtx, - publicSplAmount: new BN(amount), + publicAmountSpl: _publicSplAmount, action: Action.UNSHIELD, - publicSolAmount: new BN(extraSolAmount), - recipientFee: tokenCtx.isSol ? recipient : AUTHORITY, // TODO: check needs token account? // recipient of spl - recipientSPLAddress: recipientSPLAddress - ? recipientSPLAddress - : undefined, + publicAmountSol: _publicSolAmount, + recipientFee: recipientSol ? recipientSol : AUTHORITY, + recipientSPLAddress: recipientSpl ? recipientSpl : undefined, + ataCreationFee, }); let tx = new Transaction({ @@ -502,22 +623,22 @@ export class User { }); await tx.compileAndProve(); + let txHash; try { - let res = await tx.sendAndConfirmTransaction(); - console.log( - `https://explorer.solana.com/tx/${res}?cluster=custom&customUrl=http%3A%2F%2Flocalhost%3A8899`, - ); + txHash = await tx.sendAndConfirmTransaction(); } catch (e) { - throw new Error(`Error in tx.sendAndConfirmTransaction! ${e}`); + throw new UserError( + TransactionErrorCode.SEND_TRANSACTION_FAILED, + "shield", + `Error in tx.sendAndConfirmTransaction! ${e}`, + ); } - // await tx.checkBalances(); - console.log("checkBalances INACTIVE"); + + let response; if (this.provider.browserWallet) { - const response = await axios.post( - "http://localhost:3331/updatemerkletree", - ); - console.log({ response }); + response = await axios.post("http://localhost:3331/updatemerkletree"); } + return { txHash, response }; } // TODO: add separate lookup function for users. @@ -532,44 +653,54 @@ export class User { */ async transfer({ token, - amount, - recipient, // shieldedaddress - recipientEncryptionPublicKey, - extraSolAmount, + // alternatively we could use the recipient type here as well + recipient, + amountSpl, + amountSol, }: { token: string; - amount: number; - recipient: string; - recipientEncryptionPublicKey: Uint8Array; - extraSolAmount?: number; + amountSpl?: BN | number | string; + amountSol?: BN | number | string; + recipient: Account; }) { - const tokenCtx = TOKEN_REGISTRY.find((t) => t.symbol === token); - if (!tokenCtx) throw new Error("This token is not supported!"); + if (!recipient) + throw new UserError( + UserErrorCode.SHIELDED_RECIPIENT_UNDEFINED, + "transfer", + "No shielded recipient provided for transfer.", + ); - amount = amount * tokenCtx.decimals; + if (!amountSol && !amountSpl) + throw new UserError( + UserErrorCode.NO_AMOUNTS_PROVIDED, + "transfer", + "Need to provide at least one amount for an unshield", + ); + const tokenCtx = TOKEN_REGISTRY.find((t) => t.symbol === token); + if (!tokenCtx) + throw new UserError( + UserErrorCode.TOKEN_NOT_FOUND, + "transfer", + "Token not supported!", + ); - if (!tokenCtx.isSol) { - extraSolAmount = extraSolAmount ? extraSolAmount * 1e9 : MINIMUM_LAMPORTS; - } else { - extraSolAmount = amount; - amount = 0; - } - const _recipient: Uint8Array = strToArr(recipient.toString()); + var parsedSplAmount: BN = amountSpl + ? convertAndComputeDecimals(amountSpl, tokenCtx.decimals) + : new BN(0); + // if no sol amount by default min amount if disabled 0 + const parsedSolAmount = amountSol + ? convertAndComputeDecimals(amountSol, new BN(1e9)) + : new BN(0); - const recipientAccount = Account.fromPubkey( - _recipient, - recipientEncryptionPublicKey, - this.provider.poseidon, - ); const txParams = await this.getTxParams({ tokenCtx, action: Action.TRANSFER, shieldedRecipients: [ { mint: tokenCtx.tokenAccount, - account: recipientAccount, - solAmount: new BN(extraSolAmount.toString()), - splAmount: new BN(amount.toString()), + account: recipient, + solAmount: parsedSolAmount, + splAmount: parsedSplAmount, }, ], }); @@ -580,22 +711,21 @@ export class User { await tx.compileAndProve(); + let txHash; try { - let res = await tx.sendAndConfirmTransaction(); - console.log( - `https://explorer.solana.com/tx/${res}?cluster=custom&customUrl=http%3A%2F%2Flocalhost%3A8899`, - ); + txHash = await tx.sendAndConfirmTransaction(); } catch (e) { - throw new Error(`Error in tx.sendAndConfirmTransaction! ${e}`); + throw new UserError( + TransactionErrorCode.SEND_TRANSACTION_FAILED, + "shield", + `Error in tx.sendAndConfirmTransaction! ${e}`, + ); } - //@ts-ignore - // await tx.checkBalances(); + let response; if (this.provider.browserWallet) { - const response = await axios.post( - "http://localhost:3331/updatemerkletree", - ); - console.log({ response }); + response = await axios.post("http://localhost:3331/updatemerkletree"); } + return { txHash, response }; } appInteraction() { @@ -613,6 +743,7 @@ export class User { */ // TODO: consider removing payer property completely -> let user pass in the payer for 'load' and for 'shield' only. + // TODO: evaluate whether we could use an offline instance of user, for example to generate a proof offline, also could use this to move error test to sdk /** * * @param cachedUser - optional cached user object @@ -623,12 +754,21 @@ export class User { if (cachedUser) { this.seed = cachedUser.seed; this.utxos = cachedUser.utxos; // TODO: potentially add encr/decryption - if (!provider) throw new Error("No provider provided"); + if (!provider) + throw new UserError( + ProviderErrorCode.PROVIDER_UNDEFINED, + "load", + "No provider provided", + ); this.provider = provider; } if (!this.seed) { if (this.provider.nodeWallet && this.provider?.browserWallet) - throw new Error("Both payer and browser wallet are provided"); + throw new UserError( + UserErrorCode.NO_WALLET_PROVIDED, + "load", + "No payer or browser wallet provided", + ); if (this.provider.nodeWallet) { const signature: Uint8Array = sign.detached( message, @@ -640,7 +780,11 @@ export class User { await this.provider.browserWallet.signMessage(message); this.seed = new anchor.BN(signature).toString(); } else { - throw new Error("No payer or browser wallet provided"); + throw new UserError( + UserErrorCode.NO_WALLET_PROVIDED, + "load", + "No payer or browser wallet provided", + ); } } // get the provider? @@ -689,7 +833,11 @@ export class User { await user.load(cachedUser, provider); return user; } catch (e) { - throw new Error(`Error while loading user! ${e}`); + throw new UserError( + UserErrorCode.LOAD_ERROR, + "load", + `Error while loading user! ${e}`, + ); } } diff --git a/light-sdk-ts/tests/createOutUtxos.test.ts b/light-sdk-ts/tests/createOutUtxos.test.ts index 0ac3ee8112..12154fd105 100644 --- a/light-sdk-ts/tests/createOutUtxos.test.ts +++ b/light-sdk-ts/tests/createOutUtxos.test.ts @@ -108,8 +108,8 @@ describe("Test createOutUtxos Functional", () => { it("shield sol", async () => { let outUtxos = createOutUtxos({ publicMint: tokenCtx.tokenAccount, - publicSplAmount: new BN(0), - publicSolAmount: solAmount, + publicAmountSpl: new BN(0), + publicAmountSol: solAmount, poseidon, changeUtxoAccount: k0, action: Action.SHIELD, @@ -132,8 +132,8 @@ describe("Test createOutUtxos Functional", () => { it("shield spl", async () => { let outUtxos = createOutUtxos({ publicMint: tokenCtx.tokenAccount, - publicSplAmount: new BN(10), - publicSolAmount: new BN(0), + publicAmountSpl: new BN(10), + publicAmountSol: new BN(0), poseidon, changeUtxoAccount: k0, action: Action.SHIELD, @@ -157,8 +157,8 @@ describe("Test createOutUtxos Functional", () => { let outUtxos = createOutUtxos({ inUtxos: [utxo1], publicMint: tokenCtx.tokenAccount, - publicSplAmount: new BN(0), - publicSolAmount: solAmount, + publicAmountSpl: new BN(0), + publicAmountSol: solAmount, poseidon, changeUtxoAccount: k0, action: Action.SHIELD, @@ -182,8 +182,8 @@ describe("Test createOutUtxos Functional", () => { let outUtxos = createOutUtxos({ inUtxos: [utxo1], publicMint: tokenCtx.tokenAccount, - publicSplAmount: new BN(10), - publicSolAmount: solAmount, + publicAmountSpl: new BN(10), + publicAmountSol: solAmount, poseidon, changeUtxoAccount: k0, action: Action.SHIELD, @@ -206,8 +206,8 @@ describe("Test createOutUtxos Functional", () => { it("shield sol & spl", async () => { let outUtxos = createOutUtxos({ publicMint: tokenCtx.tokenAccount, - publicSplAmount: new BN(10), - publicSolAmount: solAmount, + publicAmountSpl: new BN(10), + publicAmountSol: solAmount, poseidon, changeUtxoAccount: k0, action: Action.SHIELD, @@ -231,8 +231,8 @@ describe("Test createOutUtxos Functional", () => { let outUtxos = createOutUtxos({ inUtxos: [utxo1], publicMint: tokenCtx.tokenAccount, - publicSplAmount: splAmount, - publicSolAmount: new BN(0), + publicAmountSpl: splAmount, + publicAmountSol: new BN(0), poseidon, relayerFee: new BN(0), changeUtxoAccount: k0, @@ -257,8 +257,8 @@ describe("Test createOutUtxos Functional", () => { let outUtxos = createOutUtxos({ inUtxos: [utxo1], publicMint: tokenCtx.tokenAccount, - publicSplAmount: splAmount, - publicSolAmount: new BN(0), + publicAmountSpl: splAmount, + publicAmountSol: new BN(0), poseidon, relayerFee, changeUtxoAccount: k0, @@ -283,8 +283,8 @@ describe("Test createOutUtxos Functional", () => { let outUtxos = createOutUtxos({ inUtxos: [utxo1], publicMint: tokenCtx.tokenAccount, - publicSplAmount: new BN(0), - publicSolAmount: solAmount, + publicAmountSpl: new BN(0), + publicAmountSol: solAmount, poseidon, relayerFee: new BN(0), changeUtxoAccount: k0, @@ -309,8 +309,8 @@ describe("Test createOutUtxos Functional", () => { let outUtxos = createOutUtxos({ inUtxos: [utxo1], publicMint: tokenCtx.tokenAccount, - publicSplAmount: new BN(0), - publicSolAmount: solAmount, + publicAmountSpl: new BN(0), + publicAmountSol: solAmount, poseidon, relayerFee, changeUtxoAccount: k0, @@ -335,8 +335,8 @@ describe("Test createOutUtxos Functional", () => { let outUtxos = createOutUtxos({ inUtxos: [utxo1], publicMint: tokenCtx.tokenAccount, - publicSplAmount: splAmount, - publicSolAmount: solAmount, + publicAmountSpl: splAmount, + publicAmountSol: solAmount, poseidon, relayerFee: new BN(0), changeUtxoAccount: k0, @@ -361,8 +361,8 @@ describe("Test createOutUtxos Functional", () => { let outUtxos = createOutUtxos({ inUtxos: [utxo1], publicMint: tokenCtx.tokenAccount, - publicSplAmount: splAmount, - publicSolAmount: solAmount, + publicAmountSpl: splAmount, + publicAmountSol: solAmount, poseidon, relayerFee, changeUtxoAccount: k0, @@ -386,9 +386,9 @@ describe("Test createOutUtxos Functional", () => { it("unshield in:1SOL + 1SPL should merge 2-1", async () => { let outUtxos = createOutUtxos({ publicMint: tokenCtx.tokenAccount, - publicSplAmount: splAmount, + publicAmountSpl: splAmount, inUtxos: [utxo1, utxoSol], - publicSolAmount: new BN(0), + publicAmountSol: new BN(0), poseidon, changeUtxoAccount: k0, action: Action.UNSHIELD, @@ -412,9 +412,9 @@ describe("Test createOutUtxos Functional", () => { it("unshield in:1SPL + 1SPL should merge 2-1", async () => { let outUtxos = createOutUtxos({ publicMint: tokenCtx.tokenAccount, - publicSplAmount: splAmount, + publicAmountSpl: splAmount, inUtxos: [utxo1, utxo1], - publicSolAmount: new BN(0), + publicAmountSol: new BN(0), poseidon, changeUtxoAccount: k0, action: Action.UNSHIELD, @@ -438,7 +438,7 @@ describe("Test createOutUtxos Functional", () => { it("transfer in:1 SPL ", async () => { let outUtxos = createOutUtxos({ publicMint: tokenCtx.tokenAccount, - publicSplAmount: new BN(0), + publicAmountSpl: new BN(0), inUtxos: [utxo1], recipients: [ { @@ -449,7 +449,7 @@ describe("Test createOutUtxos Functional", () => { }, ], relayerFee, - publicSolAmount: new BN(0), + publicAmountSol: new BN(0), poseidon, changeUtxoAccount: k0, action: Action.TRANSFER, @@ -535,9 +535,9 @@ describe("Test createOutUtxos Errors", () => { // @ts-ignore createOutUtxos({ publicMint: tokenCtx.tokenAccount, - publicSplAmount: splAmount, + publicAmountSpl: splAmount, inUtxos: [utxo1, utxoSol], - publicSolAmount: new BN(0), + publicAmountSol: new BN(0), // poseidon, changeUtxoAccount: k0, action: Action.UNSHIELD, @@ -555,9 +555,9 @@ describe("Test createOutUtxos Errors", () => { // @ts-ignore createOutUtxos({ publicMint: tokenCtx.tokenAccount, - publicSplAmount: splAmount, + publicAmountSpl: splAmount, inUtxos: [utxo1, utxoSol], - publicSolAmount: new BN(0), + publicAmountSol: new BN(0), poseidon, changeUtxoAccount: k0, action: Action.UNSHIELD, @@ -577,9 +577,9 @@ describe("Test createOutUtxos Errors", () => { // @ts-ignore createOutUtxos({ publicMint: tokenCtx.tokenAccount, - publicSplAmount: splAmount, + publicAmountSpl: splAmount, inUtxos: [utxo1, utxoSol], - publicSolAmount: new BN(0), + publicAmountSol: new BN(0), poseidon, changeUtxoAccount: k0, action: Action.UNSHIELD, @@ -599,9 +599,9 @@ describe("Test createOutUtxos Errors", () => { // @ts-ignore createOutUtxos({ publicMint: tokenCtx.tokenAccount, - publicSplAmount: splAmount, + publicAmountSpl: splAmount, inUtxos: [utxo1, utxoSol], - publicSolAmount: new BN(0), + publicAmountSol: new BN(0), poseidon, changeUtxoAccount: k0, action: Action.UNSHIELD, @@ -627,9 +627,9 @@ describe("Test createOutUtxos Errors", () => { // @ts-ignore createOutUtxos({ publicMint: tokenCtx.tokenAccount, - // publicSplAmount: splAmount, + // publicAmountSpl: splAmount, inUtxos: [utxo1, utxoSol], - // publicSolAmount: new BN(0), + // publicAmountSol: new BN(0), poseidon, changeUtxoAccount: k0, action: Action.UNSHIELD, @@ -655,9 +655,9 @@ describe("Test createOutUtxos Errors", () => { // @ts-ignore createOutUtxos({ // publicMint: tokenCtx.tokenAccount, - publicSplAmount: splAmount, + publicAmountSpl: splAmount, inUtxos: [utxo1, utxoSol], - // publicSolAmount: new BN(0), + // publicAmountSol: new BN(0), poseidon, changeUtxoAccount: k0, action: Action.UNSHIELD, @@ -684,9 +684,9 @@ describe("Test createOutUtxos Errors", () => { // @ts-ignore createOutUtxos({ publicMint: tokenCtx.tokenAccount, - publicSplAmount: splAmount, + publicAmountSpl: splAmount, inUtxos: [utxo1, utxoSol], - publicSolAmount: new BN(0), + publicAmountSol: new BN(0), poseidon, changeUtxoAccount: k0, action: Action.UNSHIELD, @@ -712,9 +712,9 @@ describe("Test createOutUtxos Errors", () => { // // @ts-ignore // createOutUtxos({ // publicMint: tokenCtx.tokenAccount, - // publicSplAmount: splAmount, + // publicAmountSpl: splAmount, // inUtxos: [utxo1, utxoSol], - // publicSolAmount: new BN(0), + // publicAmountSol: new BN(0), // poseidon, // changeUtxoAccount: k0, // action: Action.UNSHIELD, @@ -736,9 +736,9 @@ describe("Test createOutUtxos Errors", () => { expect(() => { createOutUtxos({ publicMint: tokenCtx.tokenAccount, - publicSplAmount: splAmount, + publicAmountSpl: splAmount, inUtxos: [utxo1, utxoSol0], - publicSolAmount: new BN(0), + publicAmountSol: new BN(0), poseidon, changeUtxoAccount: k0, action: Action.UNSHIELD, diff --git a/light-sdk-ts/tests/selectInUtxos.test.ts b/light-sdk-ts/tests/selectInUtxos.test.ts index aac256fc42..6e0b9e1269 100644 --- a/light-sdk-ts/tests/selectInUtxos.test.ts +++ b/light-sdk-ts/tests/selectInUtxos.test.ts @@ -125,7 +125,7 @@ describe("Test selectInUtxos Functional", () => { let selectedUtxo = selectInUtxos({ publicMint: utxo1.assets[1], relayerFee: new BN(1000), - publicSplAmount: new BN(1), + publicAmountSpl: new BN(1), utxos: inUtxos, action: Action.UNSHIELD, }); @@ -138,7 +138,7 @@ describe("Test selectInUtxos Functional", () => { let selectedUtxo = selectInUtxos({ utxos: inUtxos, relayerFee: new BN(1000), - publicSolAmount: new BN(1e7), + publicAmountSol: new BN(1e7), action: Action.UNSHIELD, }); @@ -154,8 +154,8 @@ describe("Test selectInUtxos Functional", () => { action: Action.UNSHIELD, relayerFee: new BN(1000), publicMint: utxo1.assets[1], - publicSolAmount: new BN(1e7), - publicSplAmount: new BN(1), + publicAmountSol: new BN(1e7), + publicAmountSpl: new BN(1), }); Utxo.equal(selectedUtxo[1], utxoSol); @@ -231,8 +231,8 @@ describe("Test selectInUtxos Functional", () => { utxos: inUtxos, action: Action.SHIELD, publicMint: utxo1.assets[1], - publicSolAmount: new BN(1e7), - publicSplAmount: new BN(1), + publicAmountSol: new BN(1e7), + publicAmountSpl: new BN(1), }); Utxo.equal(selectedUtxo[0], utxo1); @@ -244,7 +244,7 @@ describe("Test selectInUtxos Functional", () => { let selectedUtxo = selectInUtxos({ utxos: inUtxos, action: Action.SHIELD, - publicSolAmount: new BN(1e7), + publicAmountSol: new BN(1e7), }); Utxo.equal(selectedUtxo[0], utxoSol); @@ -258,7 +258,7 @@ describe("Test selectInUtxos Functional", () => { utxos: inUtxos, action: Action.SHIELD, publicMint: utxo1.assets[1], - publicSplAmount: new BN(1), + publicAmountSpl: new BN(1), }); Utxo.equal(selectedUtxo[0], utxo1); @@ -382,8 +382,8 @@ describe("Test selectInUtxos Errors", () => { action: Action.UNSHIELD, relayerFee: new BN(1000), // publicMint: utxo1.assets[1], - publicSolAmount: new BN(1e7), - publicSplAmount: new BN(1), + publicAmountSol: new BN(1e7), + publicAmountSpl: new BN(1), }); }) .to.throw(SelectInUtxosError) @@ -402,8 +402,8 @@ describe("Test selectInUtxos Errors", () => { action: Action.UNSHIELD, relayerFee: new BN(1000), publicMint: utxo1.assets[1], - publicSolAmount: new BN(1e7), - // publicSplAmount: new BN(1), + publicAmountSol: new BN(1e7), + // publicAmountSpl: new BN(1), }); }) .to.throw(SelectInUtxosError) @@ -422,8 +422,8 @@ describe("Test selectInUtxos Errors", () => { action: Action.UNSHIELD, // relayerFee: new BN(1000), publicMint: utxo1.assets[1], - publicSolAmount: new BN(1e7), - publicSplAmount: new BN(1), + publicAmountSol: new BN(1e7), + publicAmountSpl: new BN(1), }); }) .to.throw(SelectInUtxosError) @@ -442,8 +442,8 @@ describe("Test selectInUtxos Errors", () => { action: Action.TRANSFER, // relayerFee: new BN(1000), publicMint: utxo1.assets[1], - publicSolAmount: new BN(1e7), - publicSplAmount: new BN(1), + publicAmountSol: new BN(1e7), + publicAmountSpl: new BN(1), }); }) .to.throw(SelectInUtxosError) @@ -462,8 +462,8 @@ describe("Test selectInUtxos Errors", () => { action: Action.SHIELD, relayerFee: new BN(1000), publicMint: utxo1.assets[1], - publicSolAmount: new BN(1e7), - publicSplAmount: new BN(1), + publicAmountSol: new BN(1e7), + publicAmountSpl: new BN(1), }); }) .to.throw(SelectInUtxosError) @@ -482,8 +482,8 @@ describe("Test selectInUtxos Errors", () => { action: Action.TRANSFER, relayerFee: new BN(1000), publicMint: utxo1.assets[1], - publicSolAmount: new BN(1e7), - publicSplAmount: new BN(1), + publicAmountSol: new BN(1e7), + publicAmountSpl: new BN(1), }); }) .to.throw(SelectInUtxosError) diff --git a/light-system-programs/package.json b/light-system-programs/package.json index 500b92c9c4..2fb9c447a5 100644 --- a/light-system-programs/package.json +++ b/light-system-programs/package.json @@ -15,6 +15,7 @@ "@solana/spl-account-compression": "^0.1.5", "@solana/spl-token": "0.3.7", "@solana/web3.js": "1.73.2", + "chai-as-promised": "^7.1.1", "circomlibjs": "^0.1.7", "ffjavascript": "^0.2.54", "fs": "^0.0.1-security", diff --git a/light-system-programs/tests/user_tests.ts b/light-system-programs/tests/user_tests.ts index 01dc78f846..3086e7b16c 100644 --- a/light-system-programs/tests/user_tests.ts +++ b/light-system-programs/tests/user_tests.ts @@ -2,6 +2,10 @@ import * as anchor from "@coral-xyz/anchor"; import { Keypair as SolanaKeypair, PublicKey, SystemProgram } from "@solana/web3.js"; import _ from "lodash"; import { assert } from "chai"; +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); +// Load chai-as-promised support +chai.use(chaiAsPromised); let circomlibjs = require("circomlibjs"); @@ -23,6 +27,11 @@ import { TOKEN_REGISTRY, updateMerkleTreeForTest, createOutUtxos, + Account, + CreateUtxoErrorCode, + UserErrorCode, + TransactionErrorCode, + ADMIN_AUTH_KEY, } from "light-sdk"; import { BN } from "@coral-xyz/anchor"; @@ -32,7 +41,7 @@ var LOOK_UP_TABLE; var POSEIDON; // TODO: remove deprecated function calls -describe("verifier_program", () => { +describe("Test User", () => { // Configure the client to use the local cluster. process.env.ANCHOR_WALLET = process.env.HOME + "/.config/solana/id.json"; process.env.ANCHOR_PROVIDER_URL = "http://127.0.0.1:8899"; @@ -78,8 +87,8 @@ describe("verifier_program", () => { await provider.provider.connection.confirmTransaction(res, "confirmed"); const user: User = await User.load(provider); const preShieldedBalance = await user.getBalance({ latest: true }); - // console.log("preshieldedbalance", preShieldedBalance); - await user.shield({ amount, token, extraSolAmount: 0 }); // 2 + + await user.shield({ publicAmountSpl: amount, token }); try { console.log("updating merkle tree..."); @@ -110,9 +119,9 @@ describe("verifier_program", () => { // assert that the user's shielded balance has increased by the amount shielded assert.equal( tokenBalanceAfter.amount, - tokenBalancePre.amount + amount * tokenCtx?.decimals, + tokenBalancePre.amount.toNumber() + amount * tokenCtx?.decimals.toNumber(), `shielded balance after ${tokenBalanceAfter.amount} != shield amount ${ - amount * tokenCtx?.decimals + amount * tokenCtx?.decimals.toNumber() }`, ); @@ -136,7 +145,7 @@ describe("verifier_program", () => { // console.log("solBalanceAfter", solBalanceAfter); assert.equal( solBalanceAfter.amount, - solBalancePre.amount + 150000, //+ 2 * 1e9, this MINIMZM + solBalancePre.amount.toNumber() + 150000, //+ 2 * 1e9, this MINIMZM `shielded sol balance after ${solBalanceAfter.amount} != shield amount 0//2 aka min sol amount (50k)`, ); }); @@ -150,14 +159,14 @@ describe("verifier_program", () => { 4_000_000_000, ); await provider.provider.connection.confirmTransaction(res, "confirmed"); - const user = await User.load(provider); + const user: User = await User.load(provider); const tokenCtx = TOKEN_REGISTRY.find((t) => t.symbol === token); const preShieldedBalance = await user.getBalance({ latest: true }); const preSolBalance = await provider.provider.connection.getBalance( userKeypair.publicKey, ); - await user.shield({ amount, token }); + await user.shield({ publicAmountSol: amount, token }); // TODO: add random amount and amount checks try { console.log("updating merkle tree..."); @@ -196,16 +205,16 @@ describe("verifier_program", () => { // console.log("preSolBalance", preSolBalance); // assert that the user's shielded balance has increased by the amount shielded assert.equal( - solShieldedBalanceAfter.amount, - solShieldedBalancePre.amount + amount * tokenCtx?.decimals, + solShieldedBalanceAfter.amount.toNumber(), + solShieldedBalancePre.amount.toNumber() + amount * tokenCtx?.decimals.toNumber(), `shielded balance after ${ solShieldedBalanceAfter.amount - } != shield amount ${amount * tokenCtx?.decimals}`, + } != shield amount ${amount * tokenCtx?.decimals.toNumber()}`, ); assert.equal( postSolBalance, - preSolBalance - amount * tokenCtx.decimals + tempAccountCost, + preSolBalance - amount * tokenCtx.decimals.toNumber() + tempAccountCost, `user token balance after ${postSolBalance} != user token balance before ${preSolBalance} - shield amount ${amount} sol + tempAccountCost! ${tempAccountCost}`, ); }); @@ -247,10 +256,10 @@ describe("verifier_program", () => { ); } - const user = await User.load(provider); + const user: User = await User.load(provider); const preShieldedBalance = await user.getBalance({ latest: true }); - await user.unshield({ amount, token, recipient: solRecipient.publicKey }); + await user.unshield({ publicAmountSpl: amount, token, recipientSpl: solRecipient.publicKey }); try { console.log("updating merkle tree..."); @@ -275,10 +284,10 @@ describe("verifier_program", () => { // assert that the user's shielded balance has decreased by the amount unshielded assert.equal( - tokenBalanceAfter.amount, - tokenBalancePre.amount - amount * tokenCtx?.decimals, // TODO: check that fees go ? + tokenBalanceAfter.amount.toNumber(), + tokenBalancePre.amount.toNumber() - amount * tokenCtx?.decimals.toNumber(), // TODO: check that fees go ? `shielded balance after ${tokenBalanceAfter.amount} != unshield amount ${ - amount * tokenCtx?.decimals + amount * tokenCtx?.decimals.toNumber() }`, ); @@ -303,15 +312,15 @@ describe("verifier_program", () => { const minimumBalance = 150000; const tokenAccountFee = 500_000 assert.equal( - solBalanceAfter.amount, - solBalancePre.amount - minimumBalance - tokenAccountFee, // FIXME: no fees being charged here apparently - `shielded sol balance after ${solBalanceAfter.amount} != unshield amount ${solBalancePre.amount - minimumBalance - tokenAccountFee}`, + solBalanceAfter.amount.toNumber(), + solBalancePre.amount.toNumber() - minimumBalance - tokenAccountFee, // FIXME: no fees being charged here apparently + `shielded sol balance after ${solBalanceAfter.amount} != unshield amount ${solBalancePre.amount.toNumber() - minimumBalance - tokenAccountFee}`, ); // TODO: add checks for relayer fee recipient (log all balance changes too...) }); it("(user class) transfer SPL", async () => { - let amount = 1; + let amountSpl = 1; const token = "USDC"; const provider = await Provider.native(userKeypair); // userKeypair const shieldedRecipient = @@ -319,20 +328,25 @@ describe("verifier_program", () => { const encryptionPublicKey = "LPx24bc92eecaf5e3904bc1f4f731a2b1e0a28adf445e800c4cff112eb7a3f5350b"; + + const recipientAccount = Account.fromPubkey( + strToArr(shieldedRecipient), + strToArr(encryptionPublicKey), + POSEIDON, + ); // get token from registry const tokenCtx = TOKEN_REGISTRY.find((t) => t.symbol === token); const recipient = new anchor.BN(shieldedRecipient, "hex"); const recipientEncryptionPublicKey: Uint8Array = strToArr(encryptionPublicKey); - const user = await User.load(provider); + const user: User = await User.load(provider); const preShieldedBalance = await user.getBalance({ latest: true }); await user.transfer({ - amount, + amountSpl, token, - recipient: shieldedRecipient, - recipientEncryptionPublicKey, // TODO: do shielded address + recipient: recipientAccount, }); try { @@ -357,10 +371,10 @@ describe("verifier_program", () => { ); // assert that the user's shielded balance has decreased by the amount transferred assert.equal( - tokenBalanceAfter.amount, - tokenBalancePre.amount - amount * tokenCtx?.decimals, // TODO: check that fees go ? + tokenBalanceAfter.amount.toNumber(), + tokenBalancePre.amount.toNumber() - amountSpl * tokenCtx?.decimals.toNumber(), // TODO: check that fees go ? `shielded balance after ${tokenBalanceAfter.amount} != unshield amount ${ - amount * tokenCtx?.decimals + amountSpl * tokenCtx?.decimals.toNumber() }`, ); // assert that the user's sol shielded balance has decreased by fee @@ -372,8 +386,8 @@ describe("verifier_program", () => { ); const minimumChangeUtxoAmounts = 50000 * 3; assert.equal( - solBalanceAfter.amount, - solBalancePre.amount - 100000 - minimumChangeUtxoAmounts, // FIXME: no fees being charged here apparently + solBalanceAfter.amount.toNumber(), + solBalancePre.amount.toNumber() - 100000, // FIXME: no fees being charged here apparently `shielded sol balance after ${solBalanceAfter.amount} != unshield amount -fee -minimumSplUtxoChanges`, ); }); @@ -430,7 +444,7 @@ describe("verifier_program", () => { assert.equal( solBalanceAfter.amount, - solBalancePre.amount - 100000 - amount * tokenCtx.decimals, + solBalancePre.amount - 100000 - amount * tokenCtx.decimals.toNumber(), `shielded sol balance after ${solBalanceAfter.amount} != ${solBalancePre.amount} ...unshield amount -fee`, ); }); @@ -461,3 +475,179 @@ describe("verifier_program", () => { // TODO: add random amount and amount checks }); }); + + +describe("Test User Errors", () => { + // Configure the client to use the local cluster. + process.env.ANCHOR_WALLET = process.env.HOME + "/.config/solana/id.json"; + process.env.ANCHOR_PROVIDER_URL = "http://127.0.0.1:8899"; + + const providerAnchor = anchor.AnchorProvider.local( + "http://127.0.0.1:8899", + confirmConfig, + ); + anchor.setProvider(providerAnchor); + console.log("merkleTreeProgram: ", merkleTreeProgramId.toBase58()); + + const userKeypair = ADMIN_AUTH_KEYPAIR; //new SolanaKeypair(); + + let amount, token, provider, user; + before("init test setup Merkle tree lookup table etc ", async () => { + if((await providerAnchor.connection.getBalance(ADMIN_AUTH_KEY)) === 0) { + await createTestAccounts(providerAnchor.connection); + LOOK_UP_TABLE = await initLookUpTableFromFile(providerAnchor); + } + + POSEIDON = await circomlibjs.buildPoseidonOpt(); + amount = 20; + token = "USDC"; + + provider = await Provider.native(userKeypair); // userKeypair + let res = await provider.provider.connection.requestAirdrop( + userKeypair.publicKey, + 2_000_000_000, + ); + await provider.provider.connection.confirmTransaction(res, "confirmed"); + user = await User.load(provider); + }); + + it("NO_PUBLIC_AMOUNTS_PROVIDED shield", async () => { + await chai.assert.isRejected( + user.shield({ token }), + CreateUtxoErrorCode.NO_PUBLIC_AMOUNTS_PROVIDED + ) + }); + + it("TOKEN_UNDEFINED shield", async () => { + await chai.assert.isRejected( + // @ts-ignore + user.shield({ publicAmountSpl: amount }), + UserErrorCode.TOKEN_UNDEFINED + ) + }); + + it("INVALID_TOKEN shield", async () => { + + await chai.assert.isRejected( + // @ts-ignore + user.shield({ publicAmountSpl: amount , token: "SOL"}), + UserErrorCode.INVALID_TOKEN + ) + }); + + it("TOKEN_ACCOUNT_DEFINED shield", async () => { + + await chai.assert.isRejected( + // @ts-ignore + user.shield({ publicAmountSol: amount , token: "SOL", senderTokenAccount: SolanaKeypair.generate().publicKey}), + UserErrorCode.TOKEN_ACCOUNT_DEFINED + ) + }); + + it("TOKEN_NOT_FOUND shield", async () => { + + await chai.assert.isRejected( + // @ts-ignore + user.shield({ publicAmountSol: amount , token: "SPL"}), + UserErrorCode.TOKEN_NOT_FOUND + ) + }); + + it("TOKEN_NOT_FOUND unshield", async () => { + + await chai.assert.isRejected( + // @ts-ignore + user.unshield({ amountSol: amount , token: "SPL"}), + UserErrorCode.TOKEN_NOT_FOUND + ) + }); + + it("TOKEN_NOT_FOUND transfer", async () => { + + await chai.assert.isRejected( + // @ts-ignore + user.unshield({ amountSol: amount , token: "SPL"}), + UserErrorCode.TOKEN_NOT_FOUND + ) + }); + + it("NO_PUBLIC_AMOUNTS_PROVIDED unshield", async () => { + + await chai.assert.isRejected( + user.unshield({ token }), + CreateUtxoErrorCode.NO_PUBLIC_AMOUNTS_PROVIDED + ) + }); + + it("TOKEN_NOT_FOUND unshield", async () => { + + await chai.assert.isRejected( + // @ts-ignore + user.unshield({ }), + UserErrorCode.TOKEN_NOT_FOUND + ) + }); + + it("SOL_RECIPIENT_UNDEFINED unshield", async () => { + + await chai.assert.isRejected( + // @ts-ignore + user.unshield({ token: "SOL", publicAmountSol: new BN(1)}), + TransactionErrorCode.SOL_RECIPIENT_UNDEFINED + ) + + await chai.assert.isRejected( + // @ts-ignore + user.unshield({ token, publicAmountSol: new BN(1), publicAmountSpl: new BN(1), recipientSpl: SolanaKeypair.generate().publicKey}), + TransactionErrorCode.SOL_RECIPIENT_UNDEFINED + ) + }); + + it("SPL_RECIPIENT_UNDEFINED unshield", async () => { + + await chai.assert.isRejected( + // @ts-ignore + user.unshield({ token, publicAmountSpl: new BN(1)}), + TransactionErrorCode.SPL_RECIPIENT_UNDEFINED + ) + }); + + it("TOKEN_NOT_FOUND shield", async () => { + + await chai.assert.isRejected( + // @ts-ignore + user.shield({ publicAmountSol: SolanaKeypair.generate().publicKey}), + UserErrorCode.TOKEN_NOT_FOUND + ) + }); + + it("TOKEN_NOT_FOUND transfer", async () => { + + await chai.assert.isRejected( + // @ts-ignore + user.transfer({recipient: new Account({poseidon: POSEIDON}), amountSol: new BN(1) }), + UserErrorCode.TOKEN_NOT_FOUND + ) + }); + + it("SHIELDED_RECIPIENT_UNDEFINED transfer", async () => { + + await chai.assert.isRejected( + // @ts-ignore + user.transfer({ }), + UserErrorCode.SHIELDED_RECIPIENT_UNDEFINED + ) + }); + + it("NO_AMOUNTS_PROVIDED transfer", async () => { + + await chai.assert.isRejected( + // @ts-ignore + user.transfer({recipient: new Account({poseidon: POSEIDON}) }), + UserErrorCode.NO_AMOUNTS_PROVIDED + ) + }); + + + +}) diff --git a/light-system-programs/yarn.lock b/light-system-programs/yarn.lock index f4a915215b..479c250bb6 100644 --- a/light-system-programs/yarn.lock +++ b/light-system-programs/yarn.lock @@ -856,6 +856,13 @@ camelcase@^6.0.0, camelcase@^6.3.0: resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== +chai-as-promised@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0" + integrity sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA== + dependencies: + check-error "^1.0.2" + chai@^4.3.4: version "4.3.7" resolved "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz" From 3dcb4de39d17b82e9ad5e4d159f032f193c22810 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Thu, 23 Mar 2023 13:00:01 +0000 Subject: [PATCH 2/2] resolved rebase errors --- light-sdk-ts/src/wallet/createOutUtxos.ts | 10 ++-------- light-sdk-ts/src/wallet/provider.ts | 1 + 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/light-sdk-ts/src/wallet/createOutUtxos.ts b/light-sdk-ts/src/wallet/createOutUtxos.ts index 42baec7ede..3a61a00488 100644 --- a/light-sdk-ts/src/wallet/createOutUtxos.ts +++ b/light-sdk-ts/src/wallet/createOutUtxos.ts @@ -228,14 +228,8 @@ export function createOutUtxos({ "createOutUtxos", "Shield and relayer fee defined", ); - if (!publicSolAmount && !publicSplAmount) - throw new CreateUtxoError( - CreateUtxoErrorCode.NO_PUBLIC_AMOUNTS_PROVIDED, - "createOutUtxos", - "publicSolAmount not initialized for unshield", - ); - if (!publicSplAmount) publicSplAmount = new BN(0); - if (!publicSolAmount) publicSolAmount = new BN(0); + if (!publicAmountSpl) publicAmountSpl = new BN(0); + if (!publicAmountSol) publicAmountSol = new BN(0); let publicSplAssetIndex = assets.findIndex( (x) => x.asset.toBase58() === publicMint?.toBase58(), ); diff --git a/light-sdk-ts/src/wallet/provider.ts b/light-sdk-ts/src/wallet/provider.ts index eaa4c3c4ab..e398113066 100644 --- a/light-sdk-ts/src/wallet/provider.ts +++ b/light-sdk-ts/src/wallet/provider.ts @@ -90,6 +90,7 @@ export class Provider { ); this.confirmConfig = confirmConfig || { commitment: "confirmed" }; + this.minimumLamports = minimumLamports; if (nodeWallet) { this.nodeWallet = nodeWallet;